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");
1270 gst_message_new_async_done (GST_OBJECT_CAST (playsink),
1271 GST_CLOCK_TIME_NONE);
1272 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1273 (playsink), message);
1275 playsink->async_pending = FALSE;
1278 playsink->need_async_start = FALSE;
1281 /* try to change the state of an element. This function returns the element when
1282 * the state change could be performed. When this function returns NULL an error
1283 * occured and the element is unreffed if @unref is TRUE. */
1285 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1287 GstStateChangeReturn ret;
1290 ret = gst_element_set_state (element, GST_STATE_READY);
1291 if (ret == GST_STATE_CHANGE_FAILURE) {
1292 GST_DEBUG_OBJECT (playsink, "failed state change..");
1293 gst_element_set_state (element, GST_STATE_NULL);
1295 gst_object_unref (element);
1302 /* make the element (bin) that contains the elements needed to perform
1303 * video display. Only used for *raw* video streams.
1305 * +------------------------------------------------------------+
1307 * | +-------+ +----------+ +----------+ +---------+ |
1308 * | | queue | |colorspace| |videoscale| |videosink| |
1309 * | +-sink src-sink src-sink src-sink | |
1310 * | | +-------+ +----------+ +----------+ +---------+ |
1312 * +------------------------------------------------------------+
1315 static GstPlayVideoDeinterlaceChain *
1316 gen_video_deinterlace_chain (GstPlaySink * playsink)
1318 GstPlayVideoDeinterlaceChain *chain;
1321 GstElement *head = NULL, *prev = NULL;
1323 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1324 chain->chain.playsink = playsink;
1326 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1328 /* create a bin to hold objects, as we create them we add them to this bin so
1329 * that when something goes wrong we only need to unref the bin */
1330 chain->chain.bin = gst_bin_new ("vdbin");
1331 bin = GST_BIN_CAST (chain->chain.bin);
1332 gst_object_ref_sink (bin);
1334 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1335 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1336 if (chain->conv == NULL) {
1337 post_missing_element_message (playsink, COLORSPACE);
1338 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1339 (_("Missing element '%s' - check your GStreamer installation."),
1340 COLORSPACE), ("video rendering might fail"));
1342 gst_bin_add (bin, chain->conv);
1347 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1348 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1349 if (chain->deinterlace == NULL) {
1350 post_missing_element_message (playsink, "deinterlace");
1351 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1352 (_("Missing element '%s' - check your GStreamer installation."),
1353 "deinterlace"), ("deinterlacing won't work"));
1355 gst_bin_add (bin, chain->deinterlace);
1357 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1358 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1361 head = chain->deinterlace;
1363 prev = chain->deinterlace;
1367 pad = gst_element_get_static_pad (head, "sink");
1368 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1369 gst_object_unref (pad);
1371 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1375 pad = gst_element_get_static_pad (prev, "src");
1376 chain->srcpad = gst_ghost_pad_new ("src", pad);
1377 gst_object_unref (pad);
1379 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1382 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1383 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1389 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1390 (NULL), ("Failed to configure the video deinterlace chain."));
1391 free_chain ((GstPlayChain *) chain);
1397 is_valid_color_balance_element (GstColorBalance * bal)
1399 gboolean have_brightness = FALSE;
1400 gboolean have_contrast = FALSE;
1401 gboolean have_hue = FALSE;
1402 gboolean have_saturation = FALSE;
1403 const GList *channels, *l;
1405 channels = gst_color_balance_list_channels (bal);
1406 for (l = channels; l; l = l->next) {
1407 GstColorBalanceChannel *ch = l->data;
1409 if (g_strrstr (ch->label, "BRIGHTNESS"))
1410 have_brightness = TRUE;
1411 else if (g_strrstr (ch->label, "CONTRAST"))
1412 have_contrast = TRUE;
1413 else if (g_strrstr (ch->label, "HUE"))
1415 else if (g_strrstr (ch->label, "SATURATION"))
1416 have_saturation = TRUE;
1419 return have_brightness && have_contrast && have_hue && have_saturation;
1423 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1426 GstColorBalance *cb, **cb_out = user_data;
1428 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1429 valid = is_valid_color_balance_element (cb);
1432 && gst_color_balance_get_balance_type (*cb_out) ==
1433 GST_COLOR_BALANCE_SOFTWARE) {
1434 gst_object_unref (*cb_out);
1435 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1436 } else if (!*cb_out) {
1437 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1442 static GstColorBalance *
1443 find_color_balance_element (GstElement * element)
1446 GstColorBalance *cb = NULL;
1448 if (GST_IS_COLOR_BALANCE (element)
1449 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1450 return GST_COLOR_BALANCE (gst_object_ref (element));
1451 else if (!GST_IS_BIN (element))
1454 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1455 GST_TYPE_COLOR_BALANCE);
1456 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1457 &cb) == GST_ITERATOR_RESYNC)
1458 gst_iterator_resync (it);
1459 gst_iterator_free (it);
1465 colorbalance_value_changed_cb (GstColorBalance * balance,
1466 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1471 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1472 GstColorBalanceChannel *proxy = l->data;
1474 if (g_strrstr (channel->label, proxy->label)) {
1477 /* Convert to [0, 1] range */
1480 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1481 (gdouble) channel->min_value);
1482 /* Convert to proxy range */
1484 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1485 (gdouble) proxy->min_value);
1486 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1488 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1489 playsink->colorbalance_values[i]);
1496 update_colorbalance (GstPlaySink * playsink)
1498 GstColorBalance *balance = NULL;
1502 GST_OBJECT_LOCK (playsink);
1503 if (playsink->colorbalance_element) {
1505 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1507 GST_OBJECT_UNLOCK (playsink);
1511 g_signal_handlers_block_by_func (balance,
1512 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1514 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1515 GstColorBalanceChannel *proxy = l->data;
1516 GstColorBalanceChannel *channel = NULL;
1517 const GList *channels, *k;
1519 channels = gst_color_balance_list_channels (balance);
1520 for (k = channels; k; k = k->next) {
1521 GstColorBalanceChannel *tmp = k->data;
1523 if (g_strrstr (tmp->label, proxy->label)) {
1531 gst_color_balance_set_value (balance, channel,
1532 playsink->colorbalance_values[i]);
1535 g_signal_handlers_unblock_by_func (balance,
1536 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1538 gst_object_unref (balance);
1541 /* make the element (bin) that contains the elements needed to perform
1544 * +------------------------------------------------------------+
1546 * | +-------+ +----------+ +----------+ +---------+ |
1547 * | | queue | |colorspace| |videoscale| |videosink| |
1548 * | +-sink src-sink src-sink src-sink | |
1549 * | | +-------+ +----------+ +----------+ +---------+ |
1551 * +------------------------------------------------------------+
1554 static GstPlayVideoChain *
1555 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1557 GstPlayVideoChain *chain;
1560 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1562 chain = g_new0 (GstPlayVideoChain, 1);
1563 chain->chain.playsink = playsink;
1564 chain->chain.raw = raw;
1566 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1568 if (playsink->video_sink) {
1569 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1570 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1572 /* only try fallback if no specific sink was chosen */
1573 if (chain->sink == NULL) {
1574 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1575 elem = gst_element_factory_make ("autovideosink", "videosink");
1576 chain->sink = try_element (playsink, elem, TRUE);
1578 if (chain->sink == NULL) {
1579 /* if default sink from config.h is different then try it too */
1580 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1581 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1582 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1583 chain->sink = try_element (playsink, elem, TRUE);
1587 playsink->video_sink = gst_object_ref (chain->sink);
1589 if (chain->sink == NULL)
1593 /* if we can disable async behaviour of the sink, we can avoid adding a
1594 * queue for the audio chain. */
1596 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1599 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1600 async, GST_ELEMENT_NAME (elem));
1601 g_object_set (elem, "async", async, NULL);
1602 chain->async = async;
1604 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1605 chain->async = TRUE;
1608 /* Make sure the aspect ratio is kept */
1610 gst_play_sink_find_property_sinks (playsink, chain->sink,
1611 "force-aspect-ratio", G_TYPE_BOOLEAN);
1613 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1615 /* find ts-offset element */
1616 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1617 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1620 /* create a bin to hold objects, as we create them we add them to this bin so
1621 * that when something goes wrong we only need to unref the bin */
1622 chain->chain.bin = gst_bin_new ("vbin");
1623 bin = GST_BIN_CAST (chain->chain.bin);
1624 gst_object_ref_sink (bin);
1625 gst_bin_add (bin, chain->sink);
1627 /* Get the VideoOverlay element */
1629 GstVideoOverlay *overlay = NULL;
1631 GST_OBJECT_LOCK (playsink);
1632 if (playsink->overlay_element)
1633 gst_object_unref (playsink->overlay_element);
1634 playsink->overlay_element =
1635 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1636 GST_TYPE_VIDEO_OVERLAY));
1637 if (playsink->overlay_element)
1638 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1639 GST_OBJECT_UNLOCK (playsink);
1642 if (playsink->overlay_handle_set)
1643 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1644 if (playsink->overlay_handle_events_set)
1645 gst_video_overlay_handle_events (overlay,
1646 playsink->overlay_handle_events);
1647 if (playsink->overlay_render_rectangle_set)
1648 gst_video_overlay_set_render_rectangle (overlay,
1649 playsink->overlay_x, playsink->overlay_y,
1650 playsink->overlay_width, playsink->overlay_height);
1651 gst_object_unref (overlay);
1655 /* decouple decoder from sink, this improves playback quite a lot since the
1656 * decoder can continue while the sink blocks for synchronisation. We don't
1657 * need a lot of buffers as this consumes a lot of memory and we don't want
1658 * too little because else we would be context switching too quickly. */
1659 chain->queue = gst_element_factory_make ("queue", "vqueue");
1660 if (chain->queue == NULL) {
1661 post_missing_element_message (playsink, "queue");
1662 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1663 (_("Missing element '%s' - check your GStreamer installation."),
1664 "queue"), ("video rendering might be suboptimal"));
1668 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1669 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1670 gst_bin_add (bin, chain->queue);
1671 head = prev = chain->queue;
1674 GST_OBJECT_LOCK (playsink);
1675 if (playsink->colorbalance_element) {
1676 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1677 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1678 gst_object_unref (playsink->colorbalance_element);
1680 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1681 GST_OBJECT_UNLOCK (playsink);
1683 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1684 || (!playsink->colorbalance_element
1685 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1686 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1687 gboolean use_balance = !playsink->colorbalance_element
1688 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1690 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1692 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1693 "use-converters", use_converters, "use-balance", use_balance, NULL);
1695 GST_OBJECT_LOCK (playsink);
1696 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1697 playsink->colorbalance_element =
1698 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1699 (chain->conv)->balance));
1700 GST_OBJECT_UNLOCK (playsink);
1702 gst_bin_add (bin, chain->conv);
1704 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1705 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1713 update_colorbalance (playsink);
1716 GST_DEBUG_OBJECT (playsink, "linking to sink");
1717 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1718 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1722 pad = gst_element_get_static_pad (head, "sink");
1723 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1725 /* sending audio/video flushes break stream changes when the pipeline
1726 * is paused and played again in 0.10 */
1728 gst_pad_set_event_function (chain->sinkpad,
1729 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1730 gst_pad_set_chain_function (chain->sinkpad,
1731 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1734 gst_object_unref (pad);
1735 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1742 if (!elem && !playsink->video_sink) {
1743 post_missing_element_message (playsink, "autovideosink");
1744 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1745 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1746 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1747 (_("Both autovideosink and %s elements are missing."),
1748 DEFAULT_VIDEOSINK), (NULL));
1750 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1751 (_("The autovideosink element is missing.")), (NULL));
1754 if (playsink->video_sink) {
1755 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1756 (_("Configured videosink %s is not working."),
1757 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1758 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1759 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1760 (_("Both autovideosink and %s elements are not working."),
1761 DEFAULT_VIDEOSINK), (NULL));
1763 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1764 (_("The autovideosink element is not working.")), (NULL));
1767 free_chain ((GstPlayChain *) chain);
1773 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1774 (NULL), ("Failed to configure the video sink."));
1775 /* checking sink made it READY */
1776 gst_element_set_state (chain->sink, GST_STATE_NULL);
1777 /* Remove chain from the bin to allow reuse later */
1778 gst_bin_remove (bin, chain->sink);
1779 free_chain ((GstPlayChain *) chain);
1785 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1788 GstPlayVideoChain *chain;
1789 GstStateChangeReturn ret;
1791 chain = playsink->videochain;
1793 chain->chain.raw = raw;
1795 /* if the chain was active we don't do anything */
1796 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1799 /* try to set the sink element to READY again */
1800 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1801 if (ret == GST_STATE_CHANGE_FAILURE)
1804 /* Get the VideoOverlay element */
1806 GstVideoOverlay *overlay = NULL;
1808 GST_OBJECT_LOCK (playsink);
1809 if (playsink->overlay_element)
1810 gst_object_unref (playsink->overlay_element);
1811 playsink->overlay_element =
1812 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1813 GST_TYPE_VIDEO_OVERLAY));
1814 if (playsink->overlay_element)
1815 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1816 GST_OBJECT_UNLOCK (playsink);
1819 if (playsink->overlay_handle_set)
1820 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1821 if (playsink->overlay_handle_events_set)
1822 gst_video_overlay_handle_events (overlay,
1823 playsink->overlay_handle_events);
1824 if (playsink->overlay_render_rectangle_set)
1825 gst_video_overlay_set_render_rectangle (overlay,
1826 playsink->overlay_x, playsink->overlay_y,
1827 playsink->overlay_width, playsink->overlay_height);
1828 gst_object_unref (overlay);
1832 /* find ts-offset element */
1833 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1834 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1837 /* if we can disable async behaviour of the sink, we can avoid adding a
1838 * queue for the audio chain. */
1840 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1843 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1844 async, GST_ELEMENT_NAME (elem));
1845 g_object_set (elem, "async", async, NULL);
1846 chain->async = async;
1848 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1849 chain->async = TRUE;
1852 /* Make sure the aspect ratio is kept */
1854 gst_play_sink_find_property_sinks (playsink, chain->sink,
1855 "force-aspect-ratio", G_TYPE_BOOLEAN);
1857 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1859 GST_OBJECT_LOCK (playsink);
1860 if (playsink->colorbalance_element) {
1861 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1862 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1863 gst_object_unref (playsink->colorbalance_element);
1865 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1866 GST_OBJECT_UNLOCK (playsink);
1869 gboolean use_balance = !playsink->colorbalance_element
1870 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1872 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1874 GST_OBJECT_LOCK (playsink);
1875 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1876 playsink->colorbalance_element =
1877 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1878 (chain->conv)->balance));
1879 GST_OBJECT_UNLOCK (playsink);
1882 update_colorbalance (playsink);
1888 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1892 GstStructure *structure;
1893 event = gst_event_new_segment (segment);
1894 structure = gst_event_writable_structure (event);
1895 gst_structure_id_set (structure,
1896 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1901 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1902 const gchar * sink_type,
1903 gboolean * sink_ignore_wrong_state,
1904 gboolean * sink_custom_flush_finished,
1905 gboolean * sink_pending_flush, GstSegment * sink_segment)
1907 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1909 const GstStructure *structure = gst_event_get_structure (event);
1911 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1912 gchar *custom_flush;
1913 gchar *custom_flush_finish;
1915 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1916 custom_flush_finish =
1917 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1918 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1919 GST_DEBUG_OBJECT (pad,
1920 "Custom %s flush event received, marking to flush %s", sink_type,
1922 GST_PLAY_SINK_LOCK (playsink);
1923 *sink_ignore_wrong_state = TRUE;
1924 *sink_custom_flush_finished = FALSE;
1925 GST_PLAY_SINK_UNLOCK (playsink);
1926 } else if (strcmp (gst_structure_get_name (structure),
1927 custom_flush_finish) == 0) {
1928 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1930 GST_PLAY_SINK_LOCK (playsink);
1931 *sink_pending_flush = TRUE;
1932 *sink_custom_flush_finished = TRUE;
1933 GST_PLAY_SINK_UNLOCK (playsink);
1936 g_free (custom_flush);
1937 g_free (custom_flush_finish);
1938 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1939 GST_PLAY_SINK_LOCK (playsink);
1940 GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1942 gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1943 GST_PLAY_SINK_UNLOCK (playsink);
1946 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1947 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
1949 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1950 const GstSegment *segment;
1952 gst_event_parse_segment (event, &segment);
1953 GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1955 GST_PLAY_SINK_LOCK (playsink);
1956 if (sink_segment->format != segment->format) {
1957 GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1959 gst_format_get_name (sink_segment->format),
1960 gst_format_get_name (segment->format));
1961 gst_segment_init (sink_segment, segment->format);
1964 GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
1965 sink_type, sink_segment);
1966 gst_segment_copy_into (&playsink->text_segment, sink_segment);
1967 GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
1968 sink_type, sink_segment);
1969 GST_PLAY_SINK_UNLOCK (playsink);
1972 gst_event_unref (event);
1973 gst_object_unref (playsink);
1977 static GstFlowReturn
1978 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
1979 const gchar * sink_type,
1980 gboolean * sink_ignore_wrong_state,
1981 gboolean * sink_custom_flush_finished,
1982 gboolean * sink_pending_flush, GstSegment * sink_segment)
1984 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
1985 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
1988 GST_PLAY_SINK_LOCK (playsink);
1990 if (*sink_pending_flush) {
1992 GstStructure *structure;
1994 *sink_pending_flush = FALSE;
1996 GST_PLAY_SINK_UNLOCK (playsink);
1998 /* make the bin drop all cached data.
1999 * This event will be dropped on the src pad, if any. */
2000 event = gst_event_new_flush_start ();
2001 structure = gst_event_writable_structure (event);
2002 gst_structure_id_set (structure,
2003 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2005 GST_DEBUG_OBJECT (pad,
2006 "Pushing %s flush-start event with reset segment marker set: %"
2007 GST_PTR_FORMAT, sink_type, event);
2008 gst_pad_send_event (pad, event);
2010 /* make queue drop all cached data.
2011 * This event will be dropped on the src pad. */
2012 event = gst_event_new_flush_stop (TRUE);
2013 structure = gst_event_writable_structure (event);
2014 gst_structure_id_set (structure,
2015 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2017 GST_DEBUG_OBJECT (pad,
2018 "Pushing %s flush-stop event with reset segment marker set: %"
2019 GST_PTR_FORMAT, sink_type, event);
2020 gst_pad_send_event (pad, event);
2022 /* Re-sync queue segment info after flush-stop.
2023 * This event will be dropped on the src pad. */
2024 if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2027 _generate_update_newsegment_event (pad, sink_segment, &event1);
2028 GST_DEBUG_OBJECT (playsink,
2029 "Pushing segment event with reset "
2030 "segment marker set: %" GST_PTR_FORMAT, event1);
2031 gst_pad_send_event (pad, event1);
2034 GST_PLAY_SINK_UNLOCK (playsink);
2037 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2039 GST_PLAY_SINK_LOCK (playsink);
2040 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2041 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2043 if (*sink_custom_flush_finished) {
2044 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2045 "wrong state for %s", sink_type);
2046 *sink_ignore_wrong_state = FALSE;
2051 GST_PLAY_SINK_UNLOCK (playsink);
2053 gst_object_unref (playsink);
2054 gst_object_unref (tbin);
2058 /* sending audio/video flushes break stream changes when the pipeline
2059 * is paused and played again in 0.10 */
2062 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2064 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2065 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2068 ret = gst_play_sink_sink_event (pad, event, "video",
2069 &playsink->video_ignore_wrong_state,
2070 &playsink->video_custom_flush_finished,
2071 &playsink->video_pending_flush, &playsink->video_segment);
2073 gst_object_unref (playsink);
2074 gst_object_unref (tbin);
2078 static GstFlowReturn
2079 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2081 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2082 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2085 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2086 &playsink->video_ignore_wrong_state,
2087 &playsink->video_custom_flush_finished,
2088 &playsink->video_pending_flush, &playsink->video_segment);
2090 gst_object_unref (playsink);
2091 gst_object_unref (tbin);
2096 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2098 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2099 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2102 ret = gst_play_sink_sink_event (pad, event, "audio",
2103 &playsink->audio_ignore_wrong_state,
2104 &playsink->audio_custom_flush_finished,
2105 &playsink->audio_pending_flush, &playsink->audio_segment);
2107 gst_object_unref (playsink);
2108 gst_object_unref (tbin);
2112 static GstFlowReturn
2113 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2115 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2116 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2119 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2120 &playsink->audio_ignore_wrong_state,
2121 &playsink->audio_custom_flush_finished,
2122 &playsink->audio_pending_flush, &playsink->audio_segment);
2124 gst_object_unref (playsink);
2125 gst_object_unref (tbin);
2131 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2134 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2137 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2138 &playsink->text_ignore_wrong_state,
2139 &playsink->text_custom_flush_finished,
2140 &playsink->text_pending_flush, &playsink->text_segment);
2142 gst_object_unref (playsink);
2147 static GstFlowReturn
2148 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2152 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2154 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2155 &playsink->text_ignore_wrong_state,
2156 &playsink->text_custom_flush_finished,
2157 &playsink->text_pending_flush, &playsink->text_segment);
2159 gst_object_unref (playsink);
2164 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2168 const GstStructure *structure;
2170 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2172 structure = gst_event_get_structure (event);
2175 gst_structure_id_has_field (structure,
2176 _playsink_reset_segment_event_marker_id)) {
2177 /* the events marked with a reset segment marker
2178 * are sent internally to reset the queue and
2179 * must be dropped here */
2180 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2181 "segment marker set: %" GST_PTR_FORMAT, event);
2186 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
2189 gst_event_unref (event);
2193 /* make an element for playback of video with subtitles embedded.
2194 * Only used for *raw* video streams.
2196 * +--------------------------------------------+
2198 * | +--------+ +-----------------+ |
2199 * | | queue | | subtitleoverlay | |
2200 * video--src sink---video_sink | |
2201 * | +--------+ | src--src
2202 * text------------------text_sink | |
2203 * | +-----------------+ |
2204 * +--------------------------------------------+
2207 static GstPlayTextChain *
2208 gen_text_chain (GstPlaySink * playsink)
2210 GstPlayTextChain *chain;
2213 GstPad *videosinkpad, *textsinkpad, *srcpad;
2215 chain = g_new0 (GstPlayTextChain, 1);
2216 chain->chain.playsink = playsink;
2218 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2220 chain->chain.bin = gst_bin_new ("tbin");
2221 bin = GST_BIN_CAST (chain->chain.bin);
2222 gst_object_ref_sink (bin);
2224 videosinkpad = textsinkpad = srcpad = NULL;
2226 /* first try to hook the text pad to the custom sink */
2227 if (playsink->text_sink) {
2228 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2229 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2232 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2235 /* make sure the sparse subtitles don't participate in the preroll */
2236 g_object_set (elem, "async", FALSE, NULL);
2237 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2238 gst_bin_add (bin, chain->sink);
2239 /* NOTE streamsynchronizer needs streams decoupled */
2240 /* make a little queue */
2241 chain->queue = gst_element_factory_make ("queue", "subqueue");
2242 if (chain->queue == NULL) {
2243 post_missing_element_message (playsink, "queue");
2244 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2245 (_("Missing element '%s' - check your GStreamer installation."),
2246 "queue"), ("rendering might be suboptimal"));
2248 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2249 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2250 "silent", TRUE, NULL);
2251 gst_bin_add (bin, chain->queue);
2253 /* we have a custom sink, this will be our textsinkpad */
2254 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2255 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2256 /* we're all fine now and we can add the sink to the chain */
2257 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2258 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2260 GST_WARNING_OBJECT (playsink,
2261 "can't find a sink pad on custom text sink");
2262 gst_bin_remove (bin, chain->sink);
2263 gst_bin_remove (bin, chain->queue);
2265 chain->queue = NULL;
2267 /* try to set sync to true but it's no biggie when we can't */
2268 if (chain->sink && (elem =
2269 gst_play_sink_find_property_sinks (playsink, chain->sink,
2270 "sync", G_TYPE_BOOLEAN)))
2271 g_object_set (elem, "sync", TRUE, NULL);
2274 gst_bin_remove (bin, chain->sink);
2276 GST_WARNING_OBJECT (playsink,
2277 "can't find async property in custom text sink");
2280 if (textsinkpad == NULL) {
2281 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2282 (_("Custom text sink element is not usable.")),
2283 ("fallback to default subtitleoverlay"));
2287 if (textsinkpad == NULL) {
2288 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2289 /* make a little queue */
2290 chain->queue = gst_element_factory_make ("queue", "vqueue");
2291 if (chain->queue == NULL) {
2292 post_missing_element_message (playsink, "queue");
2293 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2294 (_("Missing element '%s' - check your GStreamer installation."),
2295 "queue"), ("video rendering might be suboptimal"));
2297 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2298 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2299 "silent", TRUE, NULL);
2300 gst_bin_add (bin, chain->queue);
2301 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2305 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2306 if (chain->overlay == NULL) {
2307 post_missing_element_message (playsink, "subtitleoverlay");
2308 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2309 (_("Missing element '%s' - check your GStreamer installation."),
2310 "subtitleoverlay"), ("subtitle rendering disabled"));
2312 GstElement *element;
2314 gst_bin_add (bin, chain->overlay);
2316 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2317 if (playsink->font_desc) {
2318 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2319 playsink->font_desc, NULL);
2321 if (playsink->subtitle_encoding) {
2322 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2323 playsink->subtitle_encoding, NULL);
2326 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2327 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2329 /* make another little queue to decouple streams */
2330 element = gst_element_factory_make ("queue", "subqueue");
2331 if (element == NULL) {
2332 post_missing_element_message (playsink, "queue");
2333 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2334 (_("Missing element '%s' - check your GStreamer installation."),
2335 "queue"), ("rendering might be suboptimal"));
2337 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2338 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2339 "silent", TRUE, NULL);
2340 gst_bin_add (bin, element);
2341 if (gst_element_link_pads_full (element, "src", chain->overlay,
2342 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2343 textsinkpad = gst_element_get_static_pad (element, "sink");
2344 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2346 gst_bin_remove (bin, chain->sink);
2347 gst_bin_remove (bin, chain->overlay);
2349 chain->overlay = NULL;
2350 gst_object_unref (videosinkpad);
2351 videosinkpad = NULL;
2358 if (videosinkpad == NULL) {
2359 /* if we still don't have a videosink, we don't have an overlay. the only
2360 * thing we can do is insert an identity and ghost the src
2362 chain->identity = gst_element_factory_make ("identity", "tidentity");
2363 if (chain->identity == NULL) {
2364 post_missing_element_message (playsink, "identity");
2365 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2366 (_("Missing element '%s' - check your GStreamer installation."),
2367 "identity"), (NULL));
2369 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2370 g_object_set (chain->identity, "silent", TRUE, NULL);
2371 gst_bin_add (bin, chain->identity);
2372 srcpad = gst_element_get_static_pad (chain->identity, "src");
2373 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2377 /* expose the ghostpads */
2379 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2380 gst_object_unref (videosinkpad);
2381 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2384 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2385 gst_object_unref (textsinkpad);
2387 gst_pad_set_event_function (chain->textsinkpad,
2388 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2389 gst_pad_set_chain_function (chain->textsinkpad,
2390 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2392 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2395 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2396 gst_object_unref (srcpad);
2398 gst_pad_set_event_function (chain->srcpad,
2399 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2401 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2408 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2412 g_object_get (object, "volume", &vol, NULL);
2413 playsink->volume = vol;
2415 g_object_notify (G_OBJECT (playsink), "volume");
2419 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2423 g_object_get (object, "mute", &mute, NULL);
2424 playsink->mute = mute;
2426 g_object_notify (G_OBJECT (playsink), "mute");
2429 /* make the chain that contains the elements needed to perform
2432 * We add a tee as the first element so that we can link the visualisation chain
2433 * to it when requested.
2435 * +-------------------------------------------------------------+
2437 * | +---------+ +----------+ +---------+ +---------+ |
2438 * | |audioconv| |audioscale| | volume | |audiosink| |
2439 * | +-srck src-sink src-sink src-sink | |
2440 * | | +---------+ +----------+ +---------+ +---------+ |
2442 * +-------------------------------------------------------------+
2444 static GstPlayAudioChain *
2445 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2447 GstPlayAudioChain *chain;
2449 gboolean have_volume;
2451 GstElement *head, *prev, *elem = NULL;
2453 chain = g_new0 (GstPlayAudioChain, 1);
2454 chain->chain.playsink = playsink;
2455 chain->chain.raw = raw;
2457 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2459 if (playsink->audio_sink) {
2460 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2461 playsink->audio_sink);
2462 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2464 /* only try fallback if no specific sink was chosen */
2465 if (chain->sink == NULL) {
2466 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2467 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2468 chain->sink = try_element (playsink, elem, TRUE);
2470 if (chain->sink == NULL) {
2471 /* if default sink from config.h is different then try it too */
2472 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2473 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2474 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2475 chain->sink = try_element (playsink, elem, TRUE);
2479 playsink->audio_sink = gst_object_ref (chain->sink);
2481 if (chain->sink == NULL)
2484 chain->chain.bin = gst_bin_new ("abin");
2485 bin = GST_BIN_CAST (chain->chain.bin);
2486 gst_object_ref_sink (bin);
2487 gst_bin_add (bin, chain->sink);
2489 /* we have to add a queue when we need to decouple for the video sink in
2490 * visualisations and for streamsynchronizer */
2491 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2492 chain->queue = gst_element_factory_make ("queue", "aqueue");
2493 if (chain->queue == NULL) {
2494 post_missing_element_message (playsink, "queue");
2495 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2496 (_("Missing element '%s' - check your GStreamer installation."),
2497 "queue"), ("audio playback and visualizations might not work"));
2501 g_object_set (chain->queue, "silent", TRUE, NULL);
2502 gst_bin_add (bin, chain->queue);
2503 prev = head = chain->queue;
2506 /* find ts-offset element */
2507 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2508 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2511 /* check if the sink, or something within the sink, has the volume property.
2512 * If it does we don't need to add a volume element. */
2514 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2517 chain->volume = elem;
2519 g_signal_connect (chain->volume, "notify::volume",
2520 G_CALLBACK (notify_volume_cb), playsink);
2522 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2524 chain->sink_volume = TRUE;
2525 /* if the sink also has a mute property we can use this as well. We'll only
2526 * use the mute property if there is a volume property. We can simulate the
2527 * mute with the volume otherwise. */
2529 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2532 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2533 g_signal_connect (chain->mute, "notify::mute",
2534 G_CALLBACK (notify_mute_cb), playsink);
2536 /* use the sink to control the volume and mute */
2537 if (playsink->volume_changed) {
2538 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2539 playsink->volume_changed = FALSE;
2541 if (playsink->mute_changed) {
2543 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2546 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2548 playsink->mute_changed = FALSE;
2551 /* no volume, we need to add a volume element when we can */
2552 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2553 have_volume = FALSE;
2554 chain->sink_volume = FALSE;
2557 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2558 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2559 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2560 gboolean use_volume =
2561 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2562 GST_DEBUG_OBJECT (playsink,
2563 "creating audioconvert with use-converters %d, use-volume %d",
2564 use_converters, use_volume);
2566 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2567 "use-converters", use_converters, "use-volume", use_volume, NULL);
2568 gst_bin_add (bin, chain->conv);
2570 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2571 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2578 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2579 GstPlaySinkAudioConvert *conv =
2580 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2583 chain->volume = conv->volume;
2586 g_signal_connect (chain->volume, "notify::volume",
2587 G_CALLBACK (notify_volume_cb), playsink);
2589 /* volume also has the mute property */
2590 chain->mute = chain->volume;
2591 g_signal_connect (chain->mute, "notify::mute",
2592 G_CALLBACK (notify_mute_cb), playsink);
2594 /* configure with the latest volume and mute */
2595 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2597 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2603 /* we only have to link to the previous element if we have something in
2604 * front of the sink */
2605 GST_DEBUG_OBJECT (playsink, "linking to sink");
2606 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2607 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2611 /* post a warning if we have no way to configure the volume */
2613 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2614 (_("No volume control found")), ("Volume/mute is not available"));
2617 /* and ghost the sinkpad of the headmost element */
2618 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2619 pad = gst_element_get_static_pad (head, "sink");
2620 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2622 /* sending audio/video flushes break stream changes when the pipeline
2623 * is paused and played again in 0.10 */
2625 gst_pad_set_event_function (chain->sinkpad,
2626 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2627 gst_pad_set_chain_function (chain->sinkpad,
2628 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2631 gst_object_unref (pad);
2632 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2639 if (!elem && !playsink->audio_sink) {
2640 post_missing_element_message (playsink, "autoaudiosink");
2641 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2642 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2643 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2644 (_("Both autoaudiosink and %s elements are missing."),
2645 DEFAULT_AUDIOSINK), (NULL));
2647 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2648 (_("The autoaudiosink element is missing.")), (NULL));
2651 if (playsink->audio_sink) {
2652 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2653 (_("Configured audiosink %s is not working."),
2654 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2655 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2656 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2657 (_("Both autoaudiosink and %s elements are not working."),
2658 DEFAULT_AUDIOSINK), (NULL));
2660 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2661 (_("The autoaudiosink element is not working.")), (NULL));
2664 free_chain ((GstPlayChain *) chain);
2669 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2670 (NULL), ("Failed to configure the audio sink."));
2671 /* checking sink made it READY */
2672 gst_element_set_state (chain->sink, GST_STATE_NULL);
2673 /* Remove chain from the bin to allow reuse later */
2674 gst_bin_remove (bin, chain->sink);
2675 free_chain ((GstPlayChain *) chain);
2681 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2684 GstPlayAudioChain *chain;
2685 GstStateChangeReturn ret;
2686 GstPlaySinkAudioConvert *conv;
2688 chain = playsink->audiochain;
2689 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2691 chain->chain.raw = raw;
2693 /* if the chain was active we don't do anything */
2694 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2697 /* try to set the sink element to READY again */
2698 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2699 if (ret == GST_STATE_CHANGE_FAILURE)
2702 /* find ts-offset element */
2703 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2704 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2707 /* check if the sink, or something within the sink, has the volume property.
2708 * If it does we don't need to add a volume element. */
2710 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2713 chain->volume = elem;
2715 if (playsink->volume_changed) {
2716 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2718 /* use the sink to control the volume */
2719 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2720 playsink->volume_changed = FALSE;
2723 g_signal_connect (chain->volume, "notify::volume",
2724 G_CALLBACK (notify_volume_cb), playsink);
2725 /* if the sink also has a mute property we can use this as well. We'll only
2726 * use the mute property if there is a volume property. We can simulate the
2727 * mute with the volume otherwise. */
2729 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2732 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2733 g_signal_connect (chain->mute, "notify::mute",
2734 G_CALLBACK (notify_mute_cb), playsink);
2737 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2739 /* no volume, we need to add a volume element when we can */
2740 g_object_set (chain->conv, "use-volume",
2741 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2742 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2744 /* Disconnect signals */
2745 disconnect_chain (chain, playsink);
2747 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2748 chain->volume = conv->volume;
2749 chain->mute = chain->volume;
2751 g_signal_connect (chain->volume, "notify::volume",
2752 G_CALLBACK (notify_volume_cb), playsink);
2754 g_signal_connect (chain->mute, "notify::mute",
2755 G_CALLBACK (notify_mute_cb), playsink);
2757 /* configure with the latest volume and mute */
2758 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2759 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2762 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2768 * +-------------------------------------------------------------------+
2770 * | +----------+ +------------+ +----------+ +-------+ |
2771 * | | visqueue | | audioconv | | audiores | | vis | |
2772 * | +-sink src-sink + samp src-sink src-sink src-+ |
2773 * | | +----------+ +------------+ +----------+ +-------+ | |
2775 * +-------------------------------------------------------------------+
2778 static GstPlayVisChain *
2779 gen_vis_chain (GstPlaySink * playsink)
2781 GstPlayVisChain *chain;
2787 chain = g_new0 (GstPlayVisChain, 1);
2788 chain->chain.playsink = playsink;
2790 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2792 chain->chain.bin = gst_bin_new ("visbin");
2793 bin = GST_BIN_CAST (chain->chain.bin);
2794 gst_object_ref_sink (bin);
2796 /* we're queuing raw audio here, we can remove this queue when we can disable
2797 * async behaviour in the video sink. */
2798 chain->queue = gst_element_factory_make ("queue", "visqueue");
2799 if (chain->queue == NULL)
2801 g_object_set (chain->queue, "silent", TRUE, NULL);
2802 gst_bin_add (bin, chain->queue);
2804 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2805 if (chain->conv == NULL)
2806 goto no_audioconvert;
2807 gst_bin_add (bin, chain->conv);
2809 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2810 if (chain->resample == NULL)
2811 goto no_audioresample;
2812 gst_bin_add (bin, chain->resample);
2814 /* this pad will be used for blocking the dataflow and switching the vis
2816 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2818 if (playsink->visualisation) {
2819 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2820 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2822 if (chain->vis == NULL) {
2823 GST_DEBUG_OBJECT (playsink, "trying goom");
2824 elem = gst_element_factory_make ("goom", "vis");
2825 chain->vis = try_element (playsink, elem, TRUE);
2827 if (chain->vis == NULL)
2830 gst_bin_add (bin, chain->vis);
2832 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2833 GST_PAD_LINK_CHECK_NOTHING);
2835 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2836 GST_PAD_LINK_CHECK_NOTHING);
2838 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2839 GST_PAD_LINK_CHECK_NOTHING);
2843 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2844 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2846 pad = gst_element_get_static_pad (chain->queue, "sink");
2847 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2848 gst_object_unref (pad);
2849 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2851 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2852 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2859 post_missing_element_message (playsink, "queue");
2860 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2861 (_("Missing element '%s' - check your GStreamer installation."),
2863 free_chain ((GstPlayChain *) chain);
2868 post_missing_element_message (playsink, "audioconvert");
2869 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2870 (_("Missing element '%s' - check your GStreamer installation."),
2871 "audioconvert"), ("possibly a liboil version mismatch?"));
2872 free_chain ((GstPlayChain *) chain);
2877 post_missing_element_message (playsink, "audioresample");
2878 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2879 (_("Missing element '%s' - check your GStreamer installation."),
2880 "audioresample"), (NULL));
2881 free_chain ((GstPlayChain *) chain);
2886 post_missing_element_message (playsink, "goom");
2887 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2888 (_("Missing element '%s' - check your GStreamer installation."),
2890 free_chain ((GstPlayChain *) chain);
2895 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2896 (NULL), ("Failed to configure the visualisation element."));
2897 /* element made it to READY */
2898 gst_element_set_state (chain->vis, GST_STATE_NULL);
2899 free_chain ((GstPlayChain *) chain);
2904 /* this function is called when all the request pads are requested and when we
2905 * have to construct the final pipeline. Based on the flags we construct the
2906 * final output pipelines.
2909 gst_play_sink_reconfigure (GstPlaySink * playsink)
2912 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2914 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2916 /* assume we need nothing */
2917 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2919 GST_PLAY_SINK_LOCK (playsink);
2920 GST_OBJECT_LOCK (playsink);
2921 /* get flags, there are protected with the object lock */
2922 flags = playsink->flags;
2923 GST_OBJECT_UNLOCK (playsink);
2925 /* figure out which components we need */
2926 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2927 /* we have subtitles and we are requested to show it */
2931 GST_OBJECT_LOCK (playsink);
2932 if (playsink->overlay_element)
2933 gst_object_unref (playsink->overlay_element);
2934 playsink->overlay_element = NULL;
2936 if (playsink->colorbalance_element) {
2937 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2938 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2939 gst_object_unref (playsink->colorbalance_element);
2941 playsink->colorbalance_element = NULL;
2942 GST_OBJECT_UNLOCK (playsink);
2944 if (((flags & GST_PLAY_FLAG_VIDEO)
2945 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2946 /* we have video and we are requested to show it */
2949 /* we only deinterlace if native video is not requested and
2950 * we have raw video */
2951 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2952 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2953 need_deinterlace = TRUE;
2956 if (playsink->audio_pad) {
2957 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2960 if (playsink->audio_pad_raw) {
2961 /* only can do vis with raw uncompressed audio */
2962 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2963 /* also add video when we add visualisation */
2970 /* we have a text_pad and we need text rendering, in this case we need a
2971 * video_pad to combine the video with the text or visualizations */
2972 if (need_text && !need_video && !playsink->text_sink) {
2973 if (playsink->video_pad) {
2975 } else if (need_audio) {
2976 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2977 (_("Can't play a text file without video or visualizations.")),
2978 ("Have text pad but no video pad or visualizations"));
2981 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2982 (_("Can't play a text file without video or visualizations.")),
2983 ("Have text pad but no video pad or visualizations"));
2984 GST_PLAY_SINK_UNLOCK (playsink);
2989 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2990 need_video, need_vis, need_text);
2992 /* set up video pipeline */
2994 gboolean raw, async;
2996 /* we need a raw sink when we do vis or when we have a raw pad */
2997 raw = need_vis ? TRUE : playsink->video_pad_raw;
2998 /* we try to set the sink async=FALSE when we need vis, this way we can
2999 * avoid a queue in the audio chain. */
3002 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3003 playsink->video_pad_raw);
3005 if (playsink->videochain) {
3006 /* try to reactivate the chain */
3007 if (!setup_video_chain (playsink, raw, async)) {
3008 if (playsink->video_sinkpad_stream_synchronizer) {
3009 gst_element_release_request_pad (GST_ELEMENT_CAST
3010 (playsink->stream_synchronizer),
3011 playsink->video_sinkpad_stream_synchronizer);
3012 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3013 playsink->video_sinkpad_stream_synchronizer = NULL;
3014 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3015 playsink->video_srcpad_stream_synchronizer = NULL;
3018 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3020 /* Remove the sink from the bin to keep its state
3021 * and unparent it to allow reuse */
3022 if (playsink->videochain->sink)
3023 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3024 playsink->videochain->sink);
3026 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3027 free_chain ((GstPlayChain *) playsink->videochain);
3028 playsink->videochain = NULL;
3032 if (!playsink->videochain)
3033 playsink->videochain = gen_video_chain (playsink, raw, async);
3034 if (!playsink->videochain)
3037 if (!playsink->video_sinkpad_stream_synchronizer) {
3038 GValue item = { 0, };
3041 playsink->video_sinkpad_stream_synchronizer =
3042 gst_element_get_request_pad (GST_ELEMENT_CAST
3043 (playsink->stream_synchronizer), "sink_%u");
3044 it = gst_pad_iterate_internal_links
3045 (playsink->video_sinkpad_stream_synchronizer);
3047 gst_iterator_next (it, &item);
3048 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3049 g_value_unset (&item);
3050 g_assert (playsink->video_srcpad_stream_synchronizer);
3051 gst_iterator_free (it);
3054 if (playsink->video_pad)
3055 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3056 playsink->video_sinkpad_stream_synchronizer);
3058 if (need_deinterlace) {
3059 if (!playsink->videodeinterlacechain)
3060 playsink->videodeinterlacechain =
3061 gen_video_deinterlace_chain (playsink);
3062 if (!playsink->videodeinterlacechain)
3065 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3067 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3069 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3070 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3072 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3073 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3075 if (playsink->videodeinterlacechain) {
3076 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3077 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3082 GST_DEBUG_OBJECT (playsink, "adding video chain");
3083 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3084 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3085 /* if we are not part of vis or subtitles, set the ghostpad target */
3086 if (!need_vis && !need_text && (!playsink->textchain
3087 || !playsink->text_pad)) {
3088 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3089 if (need_deinterlace)
3090 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3091 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3093 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3094 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3097 GST_DEBUG_OBJECT (playsink, "no video needed");
3098 if (playsink->videochain) {
3099 GST_DEBUG_OBJECT (playsink, "removing video chain");
3100 if (playsink->vischain) {
3103 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3105 /* also had visualisation, release the tee srcpad before we then
3106 * unlink the video from it */
3107 if (playsink->audio_tee_vissrc) {
3108 gst_element_release_request_pad (playsink->audio_tee,
3109 playsink->audio_tee_vissrc);
3110 gst_object_unref (playsink->audio_tee_vissrc);
3111 playsink->audio_tee_vissrc = NULL;
3114 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3115 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3118 if (playsink->video_sinkpad_stream_synchronizer) {
3119 gst_element_release_request_pad (GST_ELEMENT_CAST
3120 (playsink->stream_synchronizer),
3121 playsink->video_sinkpad_stream_synchronizer);
3122 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3123 playsink->video_sinkpad_stream_synchronizer = NULL;
3124 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3125 playsink->video_srcpad_stream_synchronizer = NULL;
3128 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3129 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3130 if (playsink->videochain->ts_offset)
3131 gst_object_unref (playsink->videochain->ts_offset);
3132 playsink->videochain->ts_offset = NULL;
3135 if (playsink->videodeinterlacechain) {
3136 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3137 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3140 if (playsink->video_pad)
3141 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3147 GST_DEBUG_OBJECT (playsink, "adding audio");
3149 /* get a raw sink if we are asked for a raw pad */
3150 raw = playsink->audio_pad_raw;
3152 if (playsink->audiochain) {
3153 /* try to reactivate the chain */
3154 if (!setup_audio_chain (playsink, raw)) {
3155 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3156 if (playsink->audio_tee_asrc) {
3157 gst_element_release_request_pad (playsink->audio_tee,
3158 playsink->audio_tee_asrc);
3159 gst_object_unref (playsink->audio_tee_asrc);
3160 playsink->audio_tee_asrc = NULL;
3163 if (playsink->audio_sinkpad_stream_synchronizer) {
3164 gst_element_release_request_pad (GST_ELEMENT_CAST
3165 (playsink->stream_synchronizer),
3166 playsink->audio_sinkpad_stream_synchronizer);
3167 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3168 playsink->audio_sinkpad_stream_synchronizer = NULL;
3169 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3170 playsink->audio_srcpad_stream_synchronizer = NULL;
3173 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3175 /* Remove the sink from the bin to keep its state
3176 * and unparent it to allow reuse */
3177 if (playsink->audiochain->sink)
3178 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3179 playsink->audiochain->sink);
3181 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3182 disconnect_chain (playsink->audiochain, playsink);
3183 playsink->audiochain->volume = NULL;
3184 playsink->audiochain->mute = NULL;
3185 if (playsink->audiochain->ts_offset)
3186 gst_object_unref (playsink->audiochain->ts_offset);
3187 playsink->audiochain->ts_offset = NULL;
3188 free_chain ((GstPlayChain *) playsink->audiochain);
3189 playsink->audiochain = NULL;
3190 playsink->volume_changed = playsink->mute_changed = FALSE;
3194 if (!playsink->audiochain) {
3195 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3196 playsink->audiochain = gen_audio_chain (playsink, raw);
3199 if (!playsink->audio_sinkpad_stream_synchronizer) {
3200 GValue item = { 0, };
3203 playsink->audio_sinkpad_stream_synchronizer =
3204 gst_element_get_request_pad (GST_ELEMENT_CAST
3205 (playsink->stream_synchronizer), "sink_%u");
3206 it = gst_pad_iterate_internal_links
3207 (playsink->audio_sinkpad_stream_synchronizer);
3209 gst_iterator_next (it, &item);
3210 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3211 g_value_unset (&item);
3212 g_assert (playsink->audio_srcpad_stream_synchronizer);
3213 gst_iterator_free (it);
3216 if (playsink->audiochain) {
3217 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3218 if (playsink->audio_tee_asrc == NULL) {
3219 playsink->audio_tee_asrc =
3220 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3222 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3223 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3224 gst_pad_link_full (playsink->audio_tee_asrc,
3225 playsink->audio_sinkpad_stream_synchronizer,
3226 GST_PAD_LINK_CHECK_NOTHING);
3227 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3228 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3231 GST_DEBUG_OBJECT (playsink, "no audio needed");
3232 /* we have no audio or we are requested to not play audio */
3233 if (playsink->audiochain) {
3234 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3235 /* release the audio pad */
3236 if (playsink->audio_tee_asrc) {
3237 gst_element_release_request_pad (playsink->audio_tee,
3238 playsink->audio_tee_asrc);
3239 gst_object_unref (playsink->audio_tee_asrc);
3240 playsink->audio_tee_asrc = NULL;
3243 if (playsink->audio_sinkpad_stream_synchronizer) {
3244 gst_element_release_request_pad (GST_ELEMENT_CAST
3245 (playsink->stream_synchronizer),
3246 playsink->audio_sinkpad_stream_synchronizer);
3247 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3248 playsink->audio_sinkpad_stream_synchronizer = NULL;
3249 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3250 playsink->audio_srcpad_stream_synchronizer = NULL;
3253 if (playsink->audiochain->sink_volume) {
3254 disconnect_chain (playsink->audiochain, playsink);
3255 playsink->audiochain->volume = NULL;
3256 playsink->audiochain->mute = NULL;
3257 if (playsink->audiochain->ts_offset)
3258 gst_object_unref (playsink->audiochain->ts_offset);
3259 playsink->audiochain->ts_offset = NULL;
3261 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3262 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3269 if (!playsink->vischain)
3270 playsink->vischain = gen_vis_chain (playsink);
3272 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3274 if (playsink->vischain) {
3275 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3277 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3278 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3279 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3280 if (playsink->audio_tee_vissrc == NULL) {
3281 playsink->audio_tee_vissrc =
3282 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3284 gst_pad_link_full (playsink->audio_tee_vissrc,
3285 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3286 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3287 GST_PAD_LINK_CHECK_NOTHING);
3288 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3289 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3290 gst_object_unref (srcpad);
3293 GST_DEBUG_OBJECT (playsink, "no vis needed");
3294 if (playsink->vischain) {
3295 if (playsink->audio_tee_vissrc) {
3296 gst_element_release_request_pad (playsink->audio_tee,
3297 playsink->audio_tee_vissrc);
3298 gst_object_unref (playsink->audio_tee_vissrc);
3299 playsink->audio_tee_vissrc = NULL;
3301 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3302 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3303 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3308 GST_DEBUG_OBJECT (playsink, "adding text");
3309 if (!playsink->textchain) {
3310 GST_DEBUG_OBJECT (playsink, "creating text chain");
3311 playsink->textchain = gen_text_chain (playsink);
3313 if (playsink->textchain) {
3316 GST_DEBUG_OBJECT (playsink, "adding text chain");
3317 if (playsink->textchain->overlay)
3318 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3320 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3322 if (!playsink->text_sinkpad_stream_synchronizer) {
3323 GValue item = { 0, };
3325 playsink->text_sinkpad_stream_synchronizer =
3326 gst_element_get_request_pad (GST_ELEMENT_CAST
3327 (playsink->stream_synchronizer), "sink_%u");
3328 it = gst_pad_iterate_internal_links
3329 (playsink->text_sinkpad_stream_synchronizer);
3331 gst_iterator_next (it, &item);
3332 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3333 g_value_unset (&item);
3334 g_assert (playsink->text_srcpad_stream_synchronizer);
3335 gst_iterator_free (it);
3337 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3338 playsink->text_sinkpad_stream_synchronizer);
3339 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3340 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3343 if (need_vis || need_video) {
3348 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3349 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3350 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3351 GST_PAD_LINK_CHECK_NOTHING);
3352 gst_object_unref (srcpad);
3354 if (need_deinterlace)
3355 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3356 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3358 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3359 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3361 gst_pad_link_full (playsink->textchain->srcpad,
3362 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3365 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3368 GST_DEBUG_OBJECT (playsink, "no text needed");
3369 /* we have no subtitles/text or we are requested to not show them */
3371 if (playsink->text_sinkpad_stream_synchronizer) {
3372 gst_element_release_request_pad (GST_ELEMENT_CAST
3373 (playsink->stream_synchronizer),
3374 playsink->text_sinkpad_stream_synchronizer);
3375 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3376 playsink->text_sinkpad_stream_synchronizer = NULL;
3377 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3378 playsink->text_srcpad_stream_synchronizer = NULL;
3381 if (playsink->textchain) {
3382 if (playsink->text_pad == NULL) {
3383 /* no text pad, remove the chain entirely */
3384 GST_DEBUG_OBJECT (playsink, "removing text chain");
3385 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3386 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3388 /* we have a chain and a textpad, turn the subtitles off */
3389 GST_DEBUG_OBJECT (playsink, "turning off the text");
3390 if (playsink->textchain->overlay)
3391 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3395 if (!need_video && playsink->video_pad) {
3396 if (playsink->video_sinkpad_stream_synchronizer) {
3397 gst_element_release_request_pad (GST_ELEMENT_CAST
3398 (playsink->stream_synchronizer),
3399 playsink->video_sinkpad_stream_synchronizer);
3400 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3401 playsink->video_sinkpad_stream_synchronizer = NULL;
3402 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3403 playsink->video_srcpad_stream_synchronizer = NULL;
3406 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3409 if (playsink->text_pad && !playsink->textchain)
3410 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3412 update_av_offset (playsink);
3413 do_async_done (playsink);
3414 GST_PLAY_SINK_UNLOCK (playsink);
3421 /* gen_ chain already posted error */
3422 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3423 GST_PLAY_SINK_UNLOCK (playsink);
3429 * gst_play_sink_set_flags:
3430 * @playsink: a #GstPlaySink
3431 * @flags: #GstPlayFlags
3433 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3434 * when constructing the sink pipelins.
3436 * Returns: TRUE if the flags could be configured.
3439 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3441 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3443 GST_OBJECT_LOCK (playsink);
3444 playsink->flags = flags;
3445 GST_OBJECT_UNLOCK (playsink);
3451 * gst_play_sink_get_flags:
3452 * @playsink: a #GstPlaySink
3454 * Get the flags of @playsink. That flags control the behaviour of the sink when
3455 * it constructs the sink pipelines.
3457 * Returns: the currently configured #GstPlayFlags.
3460 gst_play_sink_get_flags (GstPlaySink * playsink)
3464 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3466 GST_OBJECT_LOCK (playsink);
3467 res = playsink->flags;
3468 GST_OBJECT_UNLOCK (playsink);
3474 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3476 GstPlayTextChain *chain;
3478 GST_PLAY_SINK_LOCK (playsink);
3479 chain = (GstPlayTextChain *) playsink->textchain;
3480 g_free (playsink->font_desc);
3481 playsink->font_desc = g_strdup (desc);
3482 if (chain && chain->overlay) {
3483 g_object_set (chain->overlay, "font-desc", desc, NULL);
3485 GST_PLAY_SINK_UNLOCK (playsink);
3489 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3491 gchar *result = NULL;
3492 GstPlayTextChain *chain;
3494 GST_PLAY_SINK_LOCK (playsink);
3495 chain = (GstPlayTextChain *) playsink->textchain;
3496 if (chain && chain->overlay) {
3497 g_object_get (chain->overlay, "font-desc", &result, NULL);
3498 playsink->font_desc = g_strdup (result);
3500 result = g_strdup (playsink->font_desc);
3502 GST_PLAY_SINK_UNLOCK (playsink);
3508 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3509 const gchar * encoding)
3511 GstPlayTextChain *chain;
3513 GST_PLAY_SINK_LOCK (playsink);
3514 chain = (GstPlayTextChain *) playsink->textchain;
3515 g_free (playsink->subtitle_encoding);
3516 playsink->subtitle_encoding = g_strdup (encoding);
3517 if (chain && chain->overlay) {
3518 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3520 GST_PLAY_SINK_UNLOCK (playsink);
3524 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3526 gchar *result = NULL;
3527 GstPlayTextChain *chain;
3529 GST_PLAY_SINK_LOCK (playsink);
3530 chain = (GstPlayTextChain *) playsink->textchain;
3531 if (chain && chain->overlay) {
3532 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3533 playsink->subtitle_encoding = g_strdup (result);
3535 result = g_strdup (playsink->subtitle_encoding);
3537 GST_PLAY_SINK_UNLOCK (playsink);
3543 update_av_offset (GstPlaySink * playsink)
3546 GstPlayAudioChain *achain;
3547 GstPlayVideoChain *vchain;
3549 av_offset = playsink->av_offset;
3550 achain = (GstPlayAudioChain *) playsink->audiochain;
3551 vchain = (GstPlayVideoChain *) playsink->videochain;
3553 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3554 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3555 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3557 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3562 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3564 GST_PLAY_SINK_LOCK (playsink);
3565 playsink->av_offset = av_offset;
3566 update_av_offset (playsink);
3567 GST_PLAY_SINK_UNLOCK (playsink);
3571 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3575 GST_PLAY_SINK_LOCK (playsink);
3576 result = playsink->av_offset;
3577 GST_PLAY_SINK_UNLOCK (playsink);
3583 * gst_play_sink_get_last_sample:
3584 * @playsink: a #GstPlaySink
3586 * Get the last displayed sample from @playsink. This sample is in the native
3587 * format of the sink element, the caps in the result sample contain the format
3588 * of the frame data.
3590 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3594 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3596 GstSample *result = NULL;
3597 GstPlayVideoChain *chain;
3599 GST_PLAY_SINK_LOCK (playsink);
3600 GST_DEBUG_OBJECT (playsink, "taking last sample");
3601 /* get the video chain if we can */
3602 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3603 GST_DEBUG_OBJECT (playsink, "found video chain");
3604 /* see if the chain is active */
3605 if (chain->chain.activated && chain->sink) {
3608 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3610 /* find and get the last-buffer property now */
3612 gst_play_sink_find_property (playsink, chain->sink,
3613 "last-sample", GST_TYPE_SAMPLE))) {
3614 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3615 g_object_get (elem, "last-sample", &result, NULL);
3616 gst_object_unref (elem);
3620 GST_PLAY_SINK_UNLOCK (playsink);
3626 * gst_play_sink_convert_sample:
3627 * @playsink: a #GstPlaySink
3630 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3631 * be in the native format of the sink element and the caps on the buffer
3632 * describe the format of the frame. If @caps is not %NULL, the video
3633 * frame will be converted to the format of the caps.
3635 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3636 * available or when the conversion failed.
3639 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3644 result = gst_play_sink_get_last_sample (playsink);
3645 if (result != NULL && caps != NULL) {
3648 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3649 if (temp == NULL && err)
3652 gst_sample_unref (result);
3660 /* I'm really uncertain whether we should make playsink post an error
3661 * on the bus or not. It's not like it's a critical issue regarding
3662 * playsink behaviour. */
3663 GST_ERROR ("Error converting frame: %s", err->message);
3664 gst_sample_unref (result);
3671 is_raw_structure (GstStructure * s)
3675 name = gst_structure_get_name (s);
3677 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3683 is_raw_pad (GstPad * pad)
3685 GstPad *peer = gst_pad_get_peer (pad);
3687 gboolean raw = TRUE;
3692 caps = gst_pad_get_current_caps (peer);
3696 caps = gst_pad_query_caps (peer, NULL);
3698 n = gst_caps_get_size (caps);
3699 for (i = 0; i < n; i++) {
3700 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3704 } else if (raw != r) {
3705 GST_ERROR_OBJECT (pad,
3706 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3712 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3714 gst_caps_unref (caps);
3715 gst_object_unref (peer);
3720 static GstPadProbeReturn
3721 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3722 gpointer user_data);
3725 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3727 if (playsink->video_pad) {
3729 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3730 (playsink->video_pad)));
3731 if (blocked && playsink->video_block_id == 0) {
3732 playsink->video_block_id =
3733 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3734 sinkpad_blocked_cb, playsink, NULL);
3735 } else if (!blocked && playsink->video_block_id) {
3736 gst_pad_remove_probe (opad, playsink->video_block_id);
3737 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3738 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3739 playsink->video_block_id = 0;
3740 playsink->video_pad_blocked = FALSE;
3742 gst_object_unref (opad);
3747 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3749 if (playsink->audio_pad) {
3751 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3752 (playsink->audio_pad)));
3753 if (blocked && playsink->audio_block_id == 0) {
3754 playsink->audio_block_id =
3755 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3756 sinkpad_blocked_cb, playsink, NULL);
3757 } else if (!blocked && playsink->audio_block_id) {
3758 gst_pad_remove_probe (opad, playsink->audio_block_id);
3759 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3760 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3761 playsink->audio_block_id = 0;
3762 playsink->audio_pad_blocked = FALSE;
3764 gst_object_unref (opad);
3769 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3771 if (playsink->text_pad) {
3773 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3774 (playsink->text_pad)));
3775 if (blocked && playsink->text_block_id == 0) {
3776 playsink->text_block_id =
3777 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3778 sinkpad_blocked_cb, playsink, NULL);
3779 } else if (!blocked && playsink->text_block_id) {
3780 gst_pad_remove_probe (opad, playsink->text_block_id);
3781 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3782 playsink->text_block_id = 0;
3783 playsink->text_pad_blocked = FALSE;
3785 gst_object_unref (opad);
3789 static GstPadProbeReturn
3790 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3793 GstPlaySink *playsink = (GstPlaySink *) user_data;
3796 GST_PLAY_SINK_LOCK (playsink);
3798 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3799 if (pad == playsink->video_pad) {
3800 playsink->video_pad_blocked = TRUE;
3801 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3802 } else if (pad == playsink->audio_pad) {
3803 playsink->audio_pad_blocked = TRUE;
3804 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3805 } else if (pad == playsink->text_pad) {
3806 playsink->text_pad_blocked = TRUE;
3807 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3810 /* We reconfigure when for ALL streams:
3811 * * there isn't a pad
3812 * * OR the pad is blocked
3813 * * OR there are no pending blocks on that pad
3816 if ((!playsink->video_pad || playsink->video_pad_blocked
3817 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3818 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3819 && (!playsink->text_pad || playsink->text_pad_blocked
3820 || !PENDING_TEXT_BLOCK (playsink))) {
3821 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3823 if (playsink->video_pad) {
3824 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3825 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3826 playsink->video_pad_raw);
3829 if (playsink->audio_pad) {
3830 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3831 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3832 playsink->audio_pad_raw);
3835 gst_play_sink_reconfigure (playsink);
3837 video_set_blocked (playsink, FALSE);
3838 audio_set_blocked (playsink, FALSE);
3839 text_set_blocked (playsink, FALSE);
3842 gst_object_unref (pad);
3844 GST_PLAY_SINK_UNLOCK (playsink);
3846 return GST_PAD_PROBE_OK;
3850 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3852 gboolean reconfigure = FALSE;
3856 g_object_get (pad, "caps", &caps, NULL);
3860 if (pad == playsink->audio_pad) {
3861 raw = is_raw_pad (pad);
3862 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3863 && playsink->audiochain;
3864 GST_DEBUG_OBJECT (pad,
3865 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3867 } else if (pad == playsink->video_pad) {
3868 raw = is_raw_pad (pad);
3869 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3870 && playsink->videochain;
3871 GST_DEBUG_OBJECT (pad,
3872 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3876 gst_caps_unref (caps);
3879 GST_PLAY_SINK_LOCK (playsink);
3880 video_set_blocked (playsink, TRUE);
3881 audio_set_blocked (playsink, TRUE);
3882 text_set_blocked (playsink, TRUE);
3883 GST_PLAY_SINK_UNLOCK (playsink);
3888 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3889 GstPlaySinkType type)
3891 gulong *block_id = NULL;
3893 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3895 GST_PLAY_SINK_LOCK (playsink);
3896 if (pad == playsink->video_pad) {
3897 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3898 type != GST_PLAY_SINK_TYPE_VIDEO)
3900 block_id = &playsink->video_block_id;
3901 } else if (pad == playsink->audio_pad) {
3902 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3903 type != GST_PLAY_SINK_TYPE_AUDIO)
3905 block_id = &playsink->audio_block_id;
3906 } else if (pad == playsink->text_pad) {
3907 if (type != GST_PLAY_SINK_TYPE_TEXT)
3909 block_id = &playsink->text_block_id;
3912 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3914 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3917 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3918 sinkpad_blocked_cb, playsink, NULL);
3919 PENDING_FLAG_SET (playsink, type);
3920 gst_object_unref (blockpad);
3922 GST_PLAY_SINK_UNLOCK (playsink);
3929 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3931 GST_PLAY_SINK_UNLOCK (playsink);
3937 * gst_play_sink_request_pad
3938 * @playsink: a #GstPlaySink
3939 * @type: a #GstPlaySinkType
3941 * Create or return a pad of @type.
3943 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3946 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3949 gboolean created = FALSE;
3950 gboolean activate = TRUE;
3951 const gchar *pad_name = NULL;
3952 gulong *block_id = NULL;
3954 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3956 GST_PLAY_SINK_LOCK (playsink);
3958 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3959 case GST_PLAY_SINK_TYPE_AUDIO:
3960 pad_name = "audio_sink";
3961 if (!playsink->audio_tee) {
3962 GST_LOG_OBJECT (playsink, "creating tee");
3963 /* create tee when needed. This element will feed the audio sink chain
3964 * and the vis chain. */
3965 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3966 if (playsink->audio_tee == NULL) {
3967 post_missing_element_message (playsink, "tee");
3968 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3969 (_("Missing element '%s' - check your GStreamer installation."),
3974 playsink->audio_tee_sink =
3975 gst_element_get_static_pad (playsink->audio_tee, "sink");
3976 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3977 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3980 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3982 if (!playsink->audio_pad) {
3983 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3984 playsink->audio_pad =
3985 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3986 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3987 G_CALLBACK (caps_notify_cb), playsink);
3990 playsink->audio_pad_raw = FALSE;
3991 res = playsink->audio_pad;
3992 block_id = &playsink->audio_block_id;
3994 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3995 case GST_PLAY_SINK_TYPE_VIDEO:
3996 pad_name = "video_sink";
3997 if (!playsink->video_pad) {
3998 GST_LOG_OBJECT (playsink, "ghosting videosink");
3999 playsink->video_pad =
4000 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4001 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4002 G_CALLBACK (caps_notify_cb), playsink);
4005 playsink->video_pad_raw = FALSE;
4006 res = playsink->video_pad;
4007 block_id = &playsink->video_block_id;
4009 case GST_PLAY_SINK_TYPE_TEXT:
4010 GST_LOG_OBJECT (playsink, "ghosting text");
4011 if (!playsink->text_pad) {
4012 playsink->text_pad =
4013 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4016 res = playsink->text_pad;
4017 block_id = &playsink->text_block_id;
4019 case GST_PLAY_SINK_TYPE_FLUSHING:
4023 /* we need a unique padname for the flushing pad. */
4024 padname = g_strdup_printf ("flushing_%u", playsink->count);
4025 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4036 GST_PLAY_SINK_UNLOCK (playsink);
4038 if (created && res) {
4039 /* we have to add the pad when it's active or we get an error when the
4040 * element is 'running' */
4041 gst_pad_set_active (res, TRUE);
4042 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4043 if (block_id && *block_id == 0) {
4045 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4048 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4049 sinkpad_blocked_cb, playsink, NULL);
4050 PENDING_FLAG_SET (playsink, type);
4051 gst_object_unref (blockpad);
4054 gst_pad_set_active (res, activate);
4062 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4063 const gchar * name, const GstCaps * caps)
4067 GstPlaySinkType type;
4068 const gchar *tplname;
4070 g_return_val_if_fail (templ != NULL, NULL);
4072 GST_DEBUG_OBJECT (element, "name:%s", name);
4074 psink = GST_PLAY_SINK (element);
4075 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4077 /* Figure out the GstPlaySinkType based on the template */
4078 if (!strcmp (tplname, "audio_sink"))
4079 type = GST_PLAY_SINK_TYPE_AUDIO;
4080 else if (!strcmp (tplname, "audio_raw_sink"))
4081 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4082 else if (!strcmp (tplname, "video_sink"))
4083 type = GST_PLAY_SINK_TYPE_VIDEO;
4084 else if (!strcmp (tplname, "video_raw_sink"))
4085 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4086 else if (!strcmp (tplname, "text_sink"))
4087 type = GST_PLAY_SINK_TYPE_TEXT;
4089 goto unknown_template;
4091 pad = gst_play_sink_request_pad (psink, type);
4095 GST_WARNING_OBJECT (element, "Unknown pad template");
4100 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4102 GstPad **res = NULL;
4103 gboolean untarget = TRUE;
4105 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4107 GST_PLAY_SINK_LOCK (playsink);
4108 if (pad == playsink->video_pad) {
4109 res = &playsink->video_pad;
4110 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4112 } else if (pad == playsink->audio_pad) {
4113 res = &playsink->audio_pad;
4114 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4116 } else if (pad == playsink->text_pad) {
4117 res = &playsink->text_pad;
4119 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4123 GST_PLAY_SINK_UNLOCK (playsink);
4126 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4127 gst_pad_set_active (*res, FALSE);
4129 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4130 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4132 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4133 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4139 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4141 GstPlaySink *psink = GST_PLAY_SINK (element);
4143 gst_play_sink_release_pad (psink, pad);
4147 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4149 GstPlaySink *playsink;
4151 playsink = GST_PLAY_SINK_CAST (bin);
4153 switch (GST_MESSAGE_TYPE (message)) {
4154 case GST_MESSAGE_STEP_DONE:
4159 gboolean flush, intermediate, eos;
4162 GST_INFO_OBJECT (playsink, "Handling step-done message");
4163 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4164 &intermediate, &duration, &eos);
4166 if (format == GST_FORMAT_BUFFERS) {
4167 /* for the buffer format, we align the other streams */
4168 if (playsink->audiochain) {
4172 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4175 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4176 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4180 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4183 case GST_MESSAGE_ELEMENT:{
4184 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4185 GstVideoOverlay *overlay;
4187 GST_OBJECT_LOCK (playsink);
4188 if (playsink->overlay_element
4189 && GST_OBJECT_CAST (playsink->overlay_element) !=
4190 GST_MESSAGE_SRC (message)) {
4191 gst_object_unref (playsink->overlay_element);
4192 playsink->overlay_element = NULL;
4195 if (!playsink->overlay_element)
4196 playsink->overlay_element =
4197 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4199 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4200 GST_OBJECT_UNLOCK (playsink);
4202 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4204 if (playsink->overlay_handle_set)
4205 gst_video_overlay_set_window_handle (playsink->overlay_element,
4206 playsink->overlay_handle);
4207 if (playsink->overlay_handle_events_set)
4208 gst_video_overlay_handle_events (playsink->overlay_element,
4209 playsink->overlay_handle_events);
4210 if (playsink->overlay_render_rectangle_set)
4211 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4212 playsink->overlay_x, playsink->overlay_y,
4213 playsink->overlay_width, playsink->overlay_height);
4215 gst_object_unref (overlay);
4216 gst_message_unref (message);
4217 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4219 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4225 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4230 /* Send an event to our sinks until one of them works; don't then send to the
4231 * remaining sinks (unlike GstBin)
4232 * Special case: If a text sink is set we need to send the event
4233 * to them in case it's source is different from the a/v stream's source.
4236 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4238 gboolean res = TRUE;
4239 if (playsink->send_event_mode == MODE_FIRST) {
4240 if (playsink->textchain && playsink->textchain->sink) {
4241 gst_event_ref (event);
4243 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4244 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4246 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4250 if (playsink->videochain) {
4251 gst_event_ref (event);
4253 gst_element_send_event (playsink->videochain->chain.bin,
4255 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4258 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4260 if (playsink->audiochain) {
4261 gst_event_ref (event);
4263 gst_element_send_event (playsink->audiochain->chain.bin,
4265 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4268 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4272 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4273 (GST_ELEMENT_CAST (playsink), event);
4277 gst_event_unref (event);
4281 /* We only want to send the event to a single sink (overriding GstBin's
4282 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4283 * events appropriately. So, this is a messy duplication of code. */
4285 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4287 gboolean res = FALSE;
4288 GstEventType event_type = GST_EVENT_TYPE (event);
4289 GstPlaySink *playsink;
4290 playsink = GST_PLAY_SINK_CAST (element);
4291 switch (event_type) {
4292 case GST_EVENT_SEEK:
4293 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4294 res = gst_play_sink_send_event_to_sink (playsink, event);
4296 case GST_EVENT_STEP:
4301 gboolean flush, intermediate;
4302 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4304 if (format == GST_FORMAT_BUFFERS) {
4305 /* for buffers, we will try to step video frames, for other formats we
4306 * send the step to all sinks */
4307 res = gst_play_sink_send_event_to_sink (playsink, event);
4310 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4317 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4324 static GstStateChangeReturn
4325 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4327 GstStateChangeReturn ret;
4328 GstStateChangeReturn bret;
4329 GstPlaySink *playsink;
4330 playsink = GST_PLAY_SINK (element);
4331 switch (transition) {
4332 case GST_STATE_CHANGE_READY_TO_PAUSED:
4333 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4335 playsink->need_async_start = TRUE;
4336 /* we want to go async to PAUSED until we managed to configure and add the
4338 do_async_start (playsink);
4339 ret = GST_STATE_CHANGE_ASYNC;
4341 /* block all pads here */
4342 GST_PLAY_SINK_LOCK (playsink);
4343 if (playsink->video_pad && playsink->video_block_id == 0) {
4345 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4346 (playsink->video_pad)));
4347 playsink->video_block_id =
4348 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4349 sinkpad_blocked_cb, playsink, NULL);
4350 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4351 gst_object_unref (opad);
4354 if (playsink->audio_pad && playsink->audio_block_id == 0) {
4356 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4357 (playsink->audio_pad)));
4358 playsink->audio_block_id =
4359 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4360 sinkpad_blocked_cb, playsink, NULL);
4361 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4362 gst_object_unref (opad);
4365 if (playsink->text_pad && playsink->text_block_id == 0) {
4367 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4368 (playsink->text_pad)));
4369 playsink->text_block_id =
4370 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4371 sinkpad_blocked_cb, playsink, NULL);
4372 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4373 gst_object_unref (opad);
4375 GST_PLAY_SINK_UNLOCK (playsink);
4377 case GST_STATE_CHANGE_PAUSED_TO_READY:
4378 /* unblock all pads here */
4379 GST_PLAY_SINK_LOCK (playsink);
4380 video_set_blocked (playsink, FALSE);
4381 audio_set_blocked (playsink, FALSE);
4382 text_set_blocked (playsink, FALSE);
4383 GST_PLAY_SINK_UNLOCK (playsink);
4385 case GST_STATE_CHANGE_READY_TO_NULL:
4386 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4387 /* remove our links to the mute and volume elements when they were
4388 * provided by a sink */
4389 disconnect_chain (playsink->audiochain, playsink);
4390 playsink->audiochain->volume = NULL;
4391 playsink->audiochain->mute = NULL;
4394 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4395 gst_object_unref (playsink->audiochain->ts_offset);
4396 playsink->audiochain->ts_offset = NULL;
4399 if (playsink->videochain && playsink->videochain->ts_offset) {
4400 gst_object_unref (playsink->videochain->ts_offset);
4401 playsink->videochain->ts_offset = NULL;
4404 GST_OBJECT_LOCK (playsink);
4405 if (playsink->overlay_element)
4406 gst_object_unref (playsink->overlay_element);
4407 playsink->overlay_element = NULL;
4409 if (playsink->colorbalance_element) {
4410 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4411 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4412 gst_object_unref (playsink->colorbalance_element);
4414 playsink->colorbalance_element = NULL;
4415 GST_OBJECT_UNLOCK (playsink);
4417 ret = GST_STATE_CHANGE_SUCCESS;
4420 /* all other state changes return SUCCESS by default, this value can be
4421 * overridden by the result of the children */
4422 ret = GST_STATE_CHANGE_SUCCESS;
4426 /* do the state change of the children */
4428 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4430 /* now look at the result of our children and adjust the return value */
4432 case GST_STATE_CHANGE_FAILURE:
4433 /* failure, we stop */
4434 goto activate_failed;
4435 case GST_STATE_CHANGE_NO_PREROLL:
4436 /* some child returned NO_PREROLL. This is strange but we never know. We
4437 * commit our async state change (if any) and return the NO_PREROLL */
4438 do_async_done (playsink);
4441 case GST_STATE_CHANGE_ASYNC:
4442 /* some child was async, return this */
4446 /* return our previously configured return value */
4450 switch (transition) {
4451 case GST_STATE_CHANGE_READY_TO_PAUSED:
4453 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4454 /* FIXME Release audio device when we implement that */
4455 playsink->need_async_start = TRUE;
4457 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4458 if (playsink->video_sinkpad_stream_synchronizer) {
4459 gst_element_release_request_pad (GST_ELEMENT_CAST
4460 (playsink->stream_synchronizer),
4461 playsink->video_sinkpad_stream_synchronizer);
4462 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4463 playsink->video_sinkpad_stream_synchronizer = NULL;
4464 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4465 playsink->video_srcpad_stream_synchronizer = NULL;
4467 if (playsink->audio_sinkpad_stream_synchronizer) {
4468 gst_element_release_request_pad (GST_ELEMENT_CAST
4469 (playsink->stream_synchronizer),
4470 playsink->audio_sinkpad_stream_synchronizer);
4471 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4472 playsink->audio_sinkpad_stream_synchronizer = NULL;
4473 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4474 playsink->audio_srcpad_stream_synchronizer = NULL;
4476 if (playsink->text_sinkpad_stream_synchronizer) {
4477 gst_element_release_request_pad (GST_ELEMENT_CAST
4478 (playsink->stream_synchronizer),
4479 playsink->text_sinkpad_stream_synchronizer);
4480 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4481 playsink->text_sinkpad_stream_synchronizer = NULL;
4482 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4483 playsink->text_srcpad_stream_synchronizer = NULL;
4487 case GST_STATE_CHANGE_READY_TO_NULL:
4488 /* remove sinks we added */
4489 if (playsink->videodeinterlacechain) {
4490 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4492 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4494 if (playsink->videochain) {
4495 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4496 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4498 if (playsink->audiochain) {
4499 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4500 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4502 if (playsink->vischain) {
4503 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4504 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4506 if (playsink->textchain) {
4507 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4508 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4510 do_async_done (playsink);
4511 /* when going to READY, keep elements around as long as possible,
4512 * so they may be re-used faster next time/url around.
4513 * when really going to NULL, clean up everything completely. */
4514 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4516 /* Unparent the sinks to allow reuse */
4517 if (playsink->videochain && playsink->videochain->sink)
4518 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4519 playsink->videochain->sink);
4520 if (playsink->audiochain && playsink->audiochain->sink)
4521 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4522 playsink->audiochain->sink);
4523 if (playsink->textchain && playsink->textchain->sink)
4524 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4525 playsink->textchain->sink);
4526 if (playsink->audio_sink != NULL)
4527 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4528 if (playsink->video_sink != NULL)
4529 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4530 if (playsink->visualisation != NULL)
4531 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4532 if (playsink->text_sink != NULL)
4533 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4534 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4535 playsink->videodeinterlacechain = NULL;
4536 free_chain ((GstPlayChain *) playsink->videochain);
4537 playsink->videochain = NULL;
4538 free_chain ((GstPlayChain *) playsink->audiochain);
4539 playsink->audiochain = NULL;
4540 free_chain ((GstPlayChain *) playsink->vischain);
4541 playsink->vischain = NULL;
4542 free_chain ((GstPlayChain *) playsink->textchain);
4543 playsink->textchain = NULL;
4553 GST_DEBUG_OBJECT (element,
4554 "element failed to change states -- activation problem?");
4555 return GST_STATE_CHANGE_FAILURE;
4560 gst_play_sink_set_property (GObject * object, guint prop_id,
4561 const GValue * value, GParamSpec * spec)
4563 GstPlaySink *playsink = GST_PLAY_SINK (object);
4566 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4569 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4572 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4574 case PROP_FONT_DESC:
4575 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4577 case PROP_SUBTITLE_ENCODING:
4578 gst_play_sink_set_subtitle_encoding (playsink,
4579 g_value_get_string (value));
4581 case PROP_VIS_PLUGIN:
4582 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4584 case PROP_AV_OFFSET:
4585 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4587 case PROP_VIDEO_SINK:
4588 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4589 g_value_get_object (value));
4591 case PROP_AUDIO_SINK:
4592 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4593 g_value_get_object (value));
4595 case PROP_TEXT_SINK:
4596 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4597 g_value_get_object (value));
4599 case PROP_SEND_EVENT_MODE:
4600 playsink->send_event_mode = g_value_get_enum (value);
4603 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4609 gst_play_sink_get_property (GObject * object, guint prop_id,
4610 GValue * value, GParamSpec * spec)
4612 GstPlaySink *playsink = GST_PLAY_SINK (object);
4615 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4618 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4621 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4623 case PROP_FONT_DESC:
4624 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4626 case PROP_SUBTITLE_ENCODING:
4627 g_value_take_string (value,
4628 gst_play_sink_get_subtitle_encoding (playsink));
4630 case PROP_VIS_PLUGIN:
4631 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4634 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4636 case PROP_AV_OFFSET:
4637 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4639 case PROP_VIDEO_SINK:
4640 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4641 GST_PLAY_SINK_TYPE_VIDEO));
4643 case PROP_AUDIO_SINK:
4644 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4645 GST_PLAY_SINK_TYPE_AUDIO));
4647 case PROP_TEXT_SINK:
4648 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4649 GST_PLAY_SINK_TYPE_TEXT));
4651 case PROP_SEND_EVENT_MODE:
4652 g_value_set_enum (value, playsink->send_event_mode);
4655 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4661 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4663 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4664 GstVideoOverlay *overlay_element;
4666 GST_OBJECT_LOCK (playsink);
4667 if (playsink->overlay_element)
4669 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4671 overlay_element = NULL;
4672 GST_OBJECT_UNLOCK (playsink);
4674 if (overlay_element) {
4675 gst_video_overlay_expose (overlay_element);
4676 gst_object_unref (overlay_element);
4681 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4682 gboolean handle_events)
4684 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4685 GstVideoOverlay *overlay_element;
4687 GST_OBJECT_LOCK (playsink);
4688 if (playsink->overlay_element)
4690 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4692 overlay_element = NULL;
4693 GST_OBJECT_UNLOCK (playsink);
4695 playsink->overlay_handle_events_set = TRUE;
4696 playsink->overlay_handle_events = handle_events;
4698 if (overlay_element) {
4699 gst_video_overlay_handle_events (overlay_element, handle_events);
4700 gst_object_unref (overlay_element);
4705 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4706 gint y, gint width, gint height)
4708 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4709 GstVideoOverlay *overlay_element;
4711 GST_OBJECT_LOCK (playsink);
4712 if (playsink->overlay_element)
4714 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4716 overlay_element = NULL;
4717 GST_OBJECT_UNLOCK (playsink);
4719 playsink->overlay_render_rectangle_set = TRUE;
4720 playsink->overlay_x = x;
4721 playsink->overlay_y = y;
4722 playsink->overlay_width = width;
4723 playsink->overlay_height = height;
4725 if (overlay_element) {
4726 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4728 gst_object_unref (overlay_element);
4733 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4736 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4737 GstVideoOverlay *overlay_element;
4739 GST_OBJECT_LOCK (playsink);
4740 if (playsink->overlay_element)
4742 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4744 overlay_element = NULL;
4745 GST_OBJECT_UNLOCK (playsink);
4747 playsink->overlay_handle_set = TRUE;
4748 playsink->overlay_handle = handle;
4750 if (overlay_element) {
4751 gst_video_overlay_set_window_handle (overlay_element, handle);
4752 gst_object_unref (overlay_element);
4757 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4759 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4760 iface->expose = gst_play_sink_overlay_expose;
4761 iface->handle_events = gst_play_sink_overlay_handle_events;
4762 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4763 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4767 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4768 GstStructure * structure)
4770 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4773 GST_PLAY_SINK_LOCK (playsink);
4774 if (playsink->videochain && playsink->videochain->chain.bin)
4775 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4776 GST_PLAY_SINK_UNLOCK (playsink);
4779 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4782 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4784 gst_object_unref (nav);
4786 GstEvent *event = gst_event_new_navigation (structure);
4788 gst_element_send_event (GST_ELEMENT (bin), event);
4791 gst_object_unref (bin);
4795 gst_structure_free (structure);
4799 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4801 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4803 iface->send_event = gst_play_sink_navigation_send_event;
4806 static const GList *
4807 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4809 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4811 return playsink->colorbalance_channels;
4815 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4816 GstColorBalanceChannel * proxy, gint value)
4818 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4821 GstColorBalance *balance_element = NULL;
4823 GST_OBJECT_LOCK (playsink);
4824 if (playsink->colorbalance_element)
4826 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4827 GST_OBJECT_UNLOCK (playsink);
4829 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4830 GstColorBalanceChannel *proxy_tmp = l->data;
4833 if (proxy_tmp != proxy)
4836 playsink->colorbalance_values[i] = value;
4838 if (balance_element) {
4839 GstColorBalanceChannel *channel = NULL;
4840 const GList *channels, *k;
4842 channels = gst_color_balance_list_channels (balance_element);
4843 for (k = channels; k; k = k->next) {
4844 GstColorBalanceChannel *tmp = l->data;
4846 if (g_strrstr (tmp->label, proxy->label)) {
4854 /* Convert to [0, 1] range */
4857 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4858 (gdouble) proxy->min_value);
4859 /* Convert to channel range */
4861 channel->min_value + new_val * ((gdouble) channel->max_value -
4862 (gdouble) channel->min_value);
4864 gst_color_balance_set_value (balance_element, channel,
4865 (gint) (new_val + 0.5));
4867 gst_object_unref (balance_element);
4870 gst_color_balance_value_changed (balance, proxy, value);
4876 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4877 GstColorBalanceChannel * proxy)
4879 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4883 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4884 GstColorBalanceChannel *proxy_tmp = l->data;
4886 if (proxy_tmp != proxy)
4889 return playsink->colorbalance_values[i];
4892 g_return_val_if_reached (0);
4895 static GstColorBalanceType
4896 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4898 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4899 GstColorBalance *balance_element = NULL;
4900 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4902 GST_OBJECT_LOCK (playsink);
4903 if (playsink->colorbalance_element)
4905 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4906 GST_OBJECT_UNLOCK (playsink);
4908 if (balance_element) {
4909 t = gst_color_balance_get_balance_type (balance_element);
4910 gst_object_unref (balance_element);
4917 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4919 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4921 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4922 iface->set_value = gst_play_sink_colorbalance_set_value;
4923 iface->get_value = gst_play_sink_colorbalance_get_value;
4924 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4928 gst_play_sink_plugin_init (GstPlugin * plugin)
4930 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4931 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4932 GST_TYPE_PLAY_SINK);