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 GstSegment text_segment;
261 gboolean custom_flush_finished;
262 gboolean ignore_wrong_state;
263 gboolean pending_flush_stop;
266 struct _GstPlaySinkClass
268 GstBinClass parent_class;
270 gboolean (*reconfigure) (GstPlaySink * playsink);
272 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
276 static GstStaticPadTemplate audiotemplate =
277 GST_STATIC_PAD_TEMPLATE ("audio_sink",
280 GST_STATIC_CAPS_ANY);
281 static GstStaticPadTemplate videotemplate =
282 GST_STATIC_PAD_TEMPLATE ("video_sink",
285 GST_STATIC_CAPS_ANY);
286 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
289 GST_STATIC_CAPS_ANY);
291 /* FIXME 0.11: Remove */
292 static GstStaticPadTemplate audiorawtemplate =
293 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
296 GST_STATIC_CAPS_ANY);
297 static GstStaticPadTemplate videorawtemplate =
298 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
301 GST_STATIC_CAPS_ANY);
312 PROP_SUBTITLE_ENCODING,
319 PROP_SEND_EVENT_MODE,
329 static void gst_play_sink_dispose (GObject * object);
330 static void gst_play_sink_finalize (GObject * object);
331 static void gst_play_sink_set_property (GObject * object, guint prop_id,
332 const GValue * value, GParamSpec * spec);
333 static void gst_play_sink_get_property (GObject * object, guint prop_id,
334 GValue * value, GParamSpec * spec);
336 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
337 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
338 static void gst_play_sink_release_request_pad (GstElement * element,
340 static gboolean gst_play_sink_send_event (GstElement * element,
342 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
343 GstStateChange transition);
345 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
347 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
348 GstPlaySink * playsink);
349 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
350 GstPlaySink * playsink);
352 static void update_av_offset (GstPlaySink * playsink);
354 static GQuark _playsink_reset_segment_event_marker_id = 0;
356 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
358 static void gst_play_sink_overlay_init (gpointer g_iface,
359 gpointer g_iface_data);
360 static void gst_play_sink_navigation_init (gpointer g_iface,
361 gpointer g_iface_data);
362 static void gst_play_sink_colorbalance_init (gpointer g_iface,
363 gpointer g_iface_data);
366 _do_init (GType type)
368 static const GInterfaceInfo svol_info = {
371 static const GInterfaceInfo ov_info = {
372 gst_play_sink_overlay_init,
375 static const GInterfaceInfo nav_info = {
376 gst_play_sink_navigation_init,
379 static const GInterfaceInfo col_info = {
380 gst_play_sink_colorbalance_init,
384 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
385 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
386 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
387 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
390 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
391 _do_init (g_define_type_id));
394 gst_play_sink_class_init (GstPlaySinkClass * klass)
396 GObjectClass *gobject_klass;
397 GstElementClass *gstelement_klass;
398 GstBinClass *gstbin_klass;
400 gobject_klass = (GObjectClass *) klass;
401 gstelement_klass = (GstElementClass *) klass;
402 gstbin_klass = (GstBinClass *) klass;
404 gobject_klass->dispose = gst_play_sink_dispose;
405 gobject_klass->finalize = gst_play_sink_finalize;
406 gobject_klass->set_property = gst_play_sink_set_property;
407 gobject_klass->get_property = gst_play_sink_get_property;
413 * Control the behaviour of playsink.
415 g_object_class_install_property (gobject_klass, PROP_FLAGS,
416 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
417 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
418 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
421 * GstPlaySink:volume:
423 * Get or set the current audio stream volume. 1.0 means 100%,
424 * 0.0 means mute. This uses a linear volume scale.
427 g_object_class_install_property (gobject_klass, PROP_VOLUME,
428 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
429 0.0, VOLUME_MAX_DOUBLE, 1.0,
430 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
431 g_object_class_install_property (gobject_klass, PROP_MUTE,
432 g_param_spec_boolean ("mute", "Mute",
433 "Mute the audio channel without changing the volume", FALSE,
434 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
435 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
436 g_param_spec_string ("subtitle-font-desc",
437 "Subtitle font description",
438 "Pango font description of font "
439 "to be used for subtitle rendering", NULL,
440 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
441 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
442 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
443 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
444 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
445 "be checked for an encoding to use. If that is not set either, "
446 "ISO-8859-15 will be assumed.", NULL,
447 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
448 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
449 g_param_spec_object ("vis-plugin", "Vis plugin",
450 "the visualization element to use (NULL = default)",
451 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
453 * GstPlaySink:sample:
455 * Get the currently rendered or prerolled sample in the video sink.
456 * The #GstCaps in the sample will describe the format of the buffer.
458 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
459 g_param_spec_boxed ("sample", "Sample",
460 "The last sample (NULL = no video available)",
461 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
463 * GstPlaySink:av-offset:
465 * Control the synchronisation offset between the audio and video streams.
466 * Positive values make the audio ahead of the video and negative values make
467 * the audio go behind the video.
471 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
472 g_param_spec_int64 ("av-offset", "AV Offset",
473 "The synchronisation offset between audio and video in nanoseconds",
474 G_MININT64, G_MAXINT64, 0,
475 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
478 * GstPlaySink:video-sink:
480 * Set the used video sink element. NULL will use the default sink. playsink
481 * must be in %GST_STATE_NULL
485 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
486 g_param_spec_object ("video-sink", "Video Sink",
487 "the video output element to use (NULL = default sink)",
488 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
490 * GstPlaySink:audio-sink:
492 * Set the used audio sink element. NULL will use the default sink. playsink
493 * must be in %GST_STATE_NULL
497 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
498 g_param_spec_object ("audio-sink", "Audio Sink",
499 "the audio output element to use (NULL = default sink)",
500 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
503 * GstPlaySink:text-sink:
505 * Set the used text sink element. NULL will use the default sink. playsink
506 * must be in %GST_STATE_NULL
510 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
511 g_param_spec_object ("text-sink", "Text sink",
512 "the text output element to use (NULL = default subtitleoverlay)",
513 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
516 * GstPlaySink::send-event-mode:
518 * Sets the handling method used for events received from send_event
519 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
520 * handling (push the event to all internal sinks).
524 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
525 g_param_spec_enum ("send-event-mode", "Send event mode",
526 "How to send events received in send_event function",
527 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
528 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
530 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
531 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
532 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
535 * GstPlaySink::convert-sample
536 * @playsink: a #GstPlaySink
537 * @caps: the target format of the sample
539 * Action signal to retrieve the currently playing video sample in the format
540 * specified by @caps.
541 * If @caps is %NULL, no conversion will be performed and this function is
542 * equivalent to the #GstPlaySink::sample property.
544 * Returns: a #GstSample of the current video sample converted to #caps.
545 * The caps in the sample will describe the final layout of the buffer data.
546 * %NULL is returned when no current sample can be retrieved or when the
549 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
550 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
551 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
552 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
554 gst_element_class_add_pad_template (gstelement_klass,
555 gst_static_pad_template_get (&audiorawtemplate));
556 gst_element_class_add_pad_template (gstelement_klass,
557 gst_static_pad_template_get (&audiotemplate));
558 gst_element_class_add_pad_template (gstelement_klass,
559 gst_static_pad_template_get (&videorawtemplate));
560 gst_element_class_add_pad_template (gstelement_klass,
561 gst_static_pad_template_get (&videotemplate));
562 gst_element_class_add_pad_template (gstelement_klass,
563 gst_static_pad_template_get (&texttemplate));
564 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
566 "Convenience sink for multiple streams",
567 "Wim Taymans <wim.taymans@gmail.com>");
569 gstelement_klass->change_state =
570 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
571 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
572 gstelement_klass->request_new_pad =
573 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
574 gstelement_klass->release_pad =
575 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
577 gstbin_klass->handle_message =
578 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
580 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
581 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
583 _playsink_reset_segment_event_marker_id =
584 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
588 gst_play_sink_init (GstPlaySink * playsink)
590 GstColorBalanceChannel *channel;
593 playsink->video_sink = NULL;
594 playsink->audio_sink = NULL;
595 playsink->visualisation = NULL;
596 playsink->text_sink = NULL;
597 playsink->volume = 1.0;
598 playsink->font_desc = NULL;
599 playsink->subtitle_encoding = NULL;
600 playsink->flags = DEFAULT_FLAGS;
601 playsink->send_event_mode = MODE_DEFAULT;
603 playsink->stream_synchronizer =
604 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
605 gst_bin_add (GST_BIN_CAST (playsink),
606 GST_ELEMENT_CAST (playsink->stream_synchronizer));
608 g_rec_mutex_init (&playsink->lock);
609 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
612 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
614 channel->label = g_strdup ("CONTRAST");
615 channel->min_value = -1000;
616 channel->max_value = 1000;
617 playsink->colorbalance_channels =
618 g_list_append (playsink->colorbalance_channels, channel);
619 playsink->colorbalance_values[0] = 0;
622 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
624 channel->label = g_strdup ("BRIGHTNESS");
625 channel->min_value = -1000;
626 channel->max_value = 1000;
627 playsink->colorbalance_channels =
628 g_list_append (playsink->colorbalance_channels, channel);
629 playsink->colorbalance_values[1] = 0;
632 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
634 channel->label = g_strdup ("HUE");
635 channel->min_value = -1000;
636 channel->max_value = 1000;
637 playsink->colorbalance_channels =
638 g_list_append (playsink->colorbalance_channels, channel);
639 playsink->colorbalance_values[2] = 0;
642 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
644 channel->label = g_strdup ("SATURATION");
645 channel->min_value = -1000;
646 channel->max_value = 1000;
647 playsink->colorbalance_channels =
648 g_list_append (playsink->colorbalance_channels, channel);
649 playsink->colorbalance_values[3] = 0;
653 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
657 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
660 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
666 free_chain (GstPlayChain * chain)
670 gst_object_unref (chain->bin);
676 gst_play_sink_dispose (GObject * object)
678 GstPlaySink *playsink;
680 playsink = GST_PLAY_SINK (object);
682 if (playsink->audio_sink != NULL) {
683 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
684 gst_object_unref (playsink->audio_sink);
685 playsink->audio_sink = NULL;
687 if (playsink->video_sink != NULL) {
688 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
689 gst_object_unref (playsink->video_sink);
690 playsink->video_sink = NULL;
692 if (playsink->visualisation != NULL) {
693 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
694 gst_object_unref (playsink->visualisation);
695 playsink->visualisation = NULL;
697 if (playsink->text_sink != NULL) {
698 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
699 gst_object_unref (playsink->text_sink);
700 playsink->text_sink = NULL;
703 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
704 playsink->videodeinterlacechain = NULL;
705 free_chain ((GstPlayChain *) playsink->videochain);
706 playsink->videochain = NULL;
707 free_chain ((GstPlayChain *) playsink->audiochain);
708 playsink->audiochain = NULL;
709 free_chain ((GstPlayChain *) playsink->vischain);
710 playsink->vischain = NULL;
711 free_chain ((GstPlayChain *) playsink->textchain);
712 playsink->textchain = NULL;
714 if (playsink->audio_tee_sink) {
715 gst_object_unref (playsink->audio_tee_sink);
716 playsink->audio_tee_sink = NULL;
719 if (playsink->audio_tee_vissrc) {
720 gst_element_release_request_pad (playsink->audio_tee,
721 playsink->audio_tee_vissrc);
722 gst_object_unref (playsink->audio_tee_vissrc);
723 playsink->audio_tee_vissrc = NULL;
726 if (playsink->audio_tee_asrc) {
727 gst_element_release_request_pad (playsink->audio_tee,
728 playsink->audio_tee_asrc);
729 gst_object_unref (playsink->audio_tee_asrc);
730 playsink->audio_tee_asrc = NULL;
733 g_free (playsink->font_desc);
734 playsink->font_desc = NULL;
736 g_free (playsink->subtitle_encoding);
737 playsink->subtitle_encoding = NULL;
739 playsink->stream_synchronizer = NULL;
741 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
743 g_list_free (playsink->colorbalance_channels);
744 playsink->colorbalance_channels = NULL;
746 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
750 gst_play_sink_finalize (GObject * object)
752 GstPlaySink *playsink;
754 playsink = GST_PLAY_SINK (object);
756 g_rec_mutex_clear (&playsink->lock);
758 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
762 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
765 GstElement **elem = NULL, *old = NULL;
767 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
769 GST_PLAY_SINK_LOCK (playsink);
771 case GST_PLAY_SINK_TYPE_AUDIO:
772 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
773 elem = &playsink->audio_sink;
775 case GST_PLAY_SINK_TYPE_VIDEO:
776 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
777 elem = &playsink->video_sink;
779 case GST_PLAY_SINK_TYPE_TEXT:
780 elem = &playsink->text_sink;
788 gst_object_ref (sink);
791 GST_PLAY_SINK_UNLOCK (playsink);
795 gst_element_set_state (old, GST_STATE_NULL);
796 gst_object_unref (old);
801 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
803 GstElement *result = NULL;
804 GstElement *elem = NULL, *chainp = NULL;
806 GST_PLAY_SINK_LOCK (playsink);
808 case GST_PLAY_SINK_TYPE_AUDIO:
809 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
811 GstPlayAudioChain *chain;
812 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
813 chainp = chain->sink;
814 elem = playsink->audio_sink;
817 case GST_PLAY_SINK_TYPE_VIDEO:
818 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
820 GstPlayVideoChain *chain;
821 if ((chain = (GstPlayVideoChain *) playsink->videochain))
822 chainp = chain->sink;
823 elem = playsink->video_sink;
826 case GST_PLAY_SINK_TYPE_TEXT:
828 GstPlayTextChain *chain;
829 if ((chain = (GstPlayTextChain *) playsink->textchain))
830 chainp = chain->sink;
831 elem = playsink->text_sink;
838 /* we have an active chain with a sink, get the sink */
839 result = gst_object_ref (chainp);
841 /* nothing found, return last configured sink */
842 if (result == NULL && elem)
843 result = gst_object_ref (elem);
844 GST_PLAY_SINK_UNLOCK (playsink);
849 static GstPadProbeReturn
850 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
853 GstPlaySink *playsink;
854 GstPlayVisChain *chain;
856 playsink = GST_PLAY_SINK (user_data);
858 GST_PLAY_SINK_LOCK (playsink);
859 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
860 /* now try to change the plugin in the running vis chain */
861 if (!(chain = (GstPlayVisChain *) playsink->vischain))
864 /* unlink the old plugin and unghost the pad */
865 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
866 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
868 /* set the old plugin to NULL and remove */
869 gst_element_set_state (chain->vis, GST_STATE_NULL);
870 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
872 /* add new plugin and set state to playing */
873 chain->vis = playsink->visualisation;
874 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
875 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
878 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
879 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
882 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
883 GST_PAD_LINK_CHECK_NOTHING);
884 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
888 GST_PLAY_SINK_UNLOCK (playsink);
890 /* remove the probe and unblock the pad */
891 return GST_PAD_PROBE_REMOVE;
895 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
897 GstPlayVisChain *chain;
899 /* setting NULL means creating the default vis plugin */
901 vis = gst_element_factory_make ("goom", "vis");
903 /* simply return if we don't have a vis plugin here */
907 GST_PLAY_SINK_LOCK (playsink);
908 /* first store the new vis */
909 if (playsink->visualisation)
910 gst_object_unref (playsink->visualisation);
912 gst_object_ref_sink (vis);
913 playsink->visualisation = vis;
915 /* now try to change the plugin in the running vis chain, if we have no chain,
916 * we don't bother, any future vis chain will be created with the new vis
918 if (!(chain = (GstPlayVisChain *) playsink->vischain))
921 /* block the pad, the next time the callback is called we can change the
922 * visualisation. It's possible that this never happens or that the pad was
923 * already blocked. If the callback never happens, we don't have new data so
924 * we don't need the new vis plugin. If the pad was already blocked, the
925 * function returns FALSE but the previous pad block will do the right thing
927 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
928 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
929 gst_play_sink_vis_blocked, playsink, NULL);
931 GST_PLAY_SINK_UNLOCK (playsink);
937 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
939 GstElement *result = NULL;
940 GstPlayVisChain *chain;
942 GST_PLAY_SINK_LOCK (playsink);
943 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
944 /* we have an active chain, get the sink */
946 result = gst_object_ref (chain->vis);
948 /* nothing found, return last configured sink */
949 if (result == NULL && playsink->visualisation)
950 result = gst_object_ref (playsink->visualisation);
951 GST_PLAY_SINK_UNLOCK (playsink);
957 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
959 GstPlayAudioChain *chain;
961 GST_PLAY_SINK_LOCK (playsink);
962 playsink->volume = volume;
963 chain = (GstPlayAudioChain *) playsink->audiochain;
964 if (chain && chain->volume) {
965 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
966 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
967 chain->mute, volume, playsink->mute);
968 /* if there is a mute element or we are not muted, set the volume */
969 if (chain->mute || !playsink->mute)
970 g_object_set (chain->volume, "volume", volume, NULL);
972 GST_LOG_OBJECT (playsink, "no volume element");
973 playsink->volume_changed = TRUE;
975 GST_PLAY_SINK_UNLOCK (playsink);
979 gst_play_sink_get_volume (GstPlaySink * playsink)
982 GstPlayAudioChain *chain;
984 GST_PLAY_SINK_LOCK (playsink);
985 chain = (GstPlayAudioChain *) playsink->audiochain;
986 result = playsink->volume;
987 if (chain && chain->volume) {
988 if (chain->mute || !playsink->mute) {
989 g_object_get (chain->volume, "volume", &result, NULL);
990 playsink->volume = result;
993 GST_PLAY_SINK_UNLOCK (playsink);
999 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1001 GstPlayAudioChain *chain;
1003 GST_PLAY_SINK_LOCK (playsink);
1004 playsink->mute = mute;
1005 chain = (GstPlayAudioChain *) playsink->audiochain;
1008 g_object_set (chain->mute, "mute", mute, NULL);
1009 } else if (chain->volume) {
1011 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1013 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1017 playsink->mute_changed = TRUE;
1019 GST_PLAY_SINK_UNLOCK (playsink);
1023 gst_play_sink_get_mute (GstPlaySink * playsink)
1026 GstPlayAudioChain *chain;
1028 GST_PLAY_SINK_LOCK (playsink);
1029 chain = (GstPlayAudioChain *) playsink->audiochain;
1030 if (chain && chain->mute) {
1031 g_object_get (chain->mute, "mute", &result, NULL);
1032 playsink->mute = result;
1034 result = playsink->mute;
1036 GST_PLAY_SINK_UNLOCK (playsink);
1042 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1046 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1047 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1051 add_chain (GstPlayChain * chain, gboolean add)
1053 if (chain->added == add)
1057 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1059 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1060 /* we don't want to lose our sink status */
1061 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1070 activate_chain (GstPlayChain * chain, gboolean activate)
1074 if (chain->activated == activate)
1077 GST_OBJECT_LOCK (chain->playsink);
1078 state = GST_STATE_TARGET (chain->playsink);
1079 GST_OBJECT_UNLOCK (chain->playsink);
1082 gst_element_set_state (chain->bin, state);
1084 gst_element_set_state (chain->bin, GST_STATE_NULL);
1086 chain->activated = activate;
1092 element_is_sink (GstElement * element)
1096 GST_OBJECT_LOCK (element);
1097 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1098 GST_OBJECT_UNLOCK (element);
1100 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1105 element_has_property (GstElement * element, const gchar * pname, GType type)
1109 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1111 if (pspec == NULL) {
1112 GST_DEBUG_OBJECT (element, "no %s property", pname);
1116 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1117 g_type_is_a (pspec->value_type, type)) {
1118 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1119 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1123 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1124 "and we expected it to be of type %s", pname,
1125 g_type_name (pspec->value_type), g_type_name (type));
1132 const gchar *prop_name;
1135 } FindPropertyHelper;
1138 find_property (const GValue * item, FindPropertyHelper * helper)
1140 GstElement *element = g_value_get_object (item);
1141 if (helper->need_sink && !element_is_sink (element)) {
1145 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1149 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1150 (helper->need_sink) ? "sink" : "element");
1151 return 0; /* keep it */
1154 /* FIXME: why not move these functions into core? */
1155 /* find a sink in the hierarchy with a property named @name. This function does
1156 * not increase the refcount of the returned object and thus remains valid as
1157 * long as the bin is valid. */
1159 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1160 const gchar * name, GType expected_type)
1162 GstElement *result = NULL;
1165 if (element_has_property (obj, name, expected_type)) {
1167 } else if (GST_IS_BIN (obj)) {
1169 GValue item = { 0, };
1170 FindPropertyHelper helper = { name, expected_type, TRUE };
1172 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1173 found = gst_iterator_find_custom (it,
1174 (GCompareFunc) find_property, &item, &helper);
1175 gst_iterator_free (it);
1177 result = g_value_get_object (&item);
1178 /* we don't need the extra ref */
1179 g_value_unset (&item);
1185 /* find an object in the hierarchy with a property named @name */
1187 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1188 const gchar * name, GType expected_type)
1190 GstElement *result = NULL;
1193 if (GST_IS_BIN (obj)) {
1195 GValue item = { 0, };
1196 FindPropertyHelper helper = { name, expected_type, FALSE };
1198 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1199 found = gst_iterator_find_custom (it,
1200 (GCompareFunc) find_property, &item, &helper);
1201 gst_iterator_free (it);
1203 result = g_value_dup_object (&item);
1204 g_value_unset (&item);
1207 if (element_has_property (obj, name, expected_type)) {
1209 gst_object_ref (obj);
1216 do_async_start (GstPlaySink * playsink)
1218 GstMessage *message;
1220 if (!playsink->need_async_start) {
1221 GST_INFO_OBJECT (playsink, "no async_start needed");
1225 playsink->async_pending = TRUE;
1227 GST_INFO_OBJECT (playsink, "Sending async_start message");
1228 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1229 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1230 (playsink), message);
1234 do_async_done (GstPlaySink * playsink)
1236 GstMessage *message;
1238 if (playsink->async_pending) {
1239 GST_INFO_OBJECT (playsink, "Sending async_done message");
1240 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1241 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1242 (playsink), message);
1244 playsink->async_pending = FALSE;
1247 playsink->need_async_start = FALSE;
1250 /* try to change the state of an element. This function returns the element when
1251 * the state change could be performed. When this function returns NULL an error
1252 * occured and the element is unreffed if @unref is TRUE. */
1254 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1256 GstStateChangeReturn ret;
1259 ret = gst_element_set_state (element, GST_STATE_READY);
1260 if (ret == GST_STATE_CHANGE_FAILURE) {
1261 GST_DEBUG_OBJECT (playsink, "failed state change..");
1262 gst_element_set_state (element, GST_STATE_NULL);
1264 gst_object_unref (element);
1271 /* make the element (bin) that contains the elements needed to perform
1272 * video display. Only used for *raw* video streams.
1274 * +------------------------------------------------------------+
1276 * | +-------+ +----------+ +----------+ +---------+ |
1277 * | | queue | |colorspace| |videoscale| |videosink| |
1278 * | +-sink src-sink src-sink src-sink | |
1279 * | | +-------+ +----------+ +----------+ +---------+ |
1281 * +------------------------------------------------------------+
1284 static GstPlayVideoDeinterlaceChain *
1285 gen_video_deinterlace_chain (GstPlaySink * playsink)
1287 GstPlayVideoDeinterlaceChain *chain;
1290 GstElement *head = NULL, *prev = NULL;
1292 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1293 chain->chain.playsink = playsink;
1295 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1297 /* create a bin to hold objects, as we create them we add them to this bin so
1298 * that when something goes wrong we only need to unref the bin */
1299 chain->chain.bin = gst_bin_new ("vdbin");
1300 bin = GST_BIN_CAST (chain->chain.bin);
1301 gst_object_ref_sink (bin);
1303 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1304 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1305 if (chain->conv == NULL) {
1306 post_missing_element_message (playsink, COLORSPACE);
1307 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1308 (_("Missing element '%s' - check your GStreamer installation."),
1309 COLORSPACE), ("video rendering might fail"));
1311 gst_bin_add (bin, chain->conv);
1316 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1317 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1318 if (chain->deinterlace == NULL) {
1319 post_missing_element_message (playsink, "deinterlace");
1320 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1321 (_("Missing element '%s' - check your GStreamer installation."),
1322 "deinterlace"), ("deinterlacing won't work"));
1324 gst_bin_add (bin, chain->deinterlace);
1326 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1327 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1330 head = chain->deinterlace;
1332 prev = chain->deinterlace;
1336 pad = gst_element_get_static_pad (head, "sink");
1337 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1338 gst_object_unref (pad);
1340 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1344 pad = gst_element_get_static_pad (prev, "src");
1345 chain->srcpad = gst_ghost_pad_new ("src", pad);
1346 gst_object_unref (pad);
1348 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1351 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1352 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1358 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1359 (NULL), ("Failed to configure the video deinterlace chain."));
1360 free_chain ((GstPlayChain *) chain);
1366 is_valid_color_balance_element (GstColorBalance * bal)
1368 gboolean have_brightness = FALSE;
1369 gboolean have_contrast = FALSE;
1370 gboolean have_hue = FALSE;
1371 gboolean have_saturation = FALSE;
1372 const GList *channels, *l;
1374 channels = gst_color_balance_list_channels (bal);
1375 for (l = channels; l; l = l->next) {
1376 GstColorBalanceChannel *ch = l->data;
1378 if (g_strrstr (ch->label, "BRIGHTNESS"))
1379 have_brightness = TRUE;
1380 else if (g_strrstr (ch->label, "CONTRAST"))
1381 have_contrast = TRUE;
1382 else if (g_strrstr (ch->label, "HUE"))
1384 else if (g_strrstr (ch->label, "SATURATION"))
1385 have_saturation = TRUE;
1388 return have_brightness && have_contrast && have_hue && have_saturation;
1392 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1395 GstColorBalance *cb, **cb_out = user_data;
1397 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1398 valid = is_valid_color_balance_element (cb);
1401 && gst_color_balance_get_balance_type (*cb_out) ==
1402 GST_COLOR_BALANCE_SOFTWARE) {
1403 gst_object_unref (*cb_out);
1404 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1405 } else if (!*cb_out) {
1406 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1411 static GstColorBalance *
1412 find_color_balance_element (GstElement * element)
1415 GstColorBalance *cb = NULL;
1417 if (GST_IS_COLOR_BALANCE (element)
1418 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1419 return GST_COLOR_BALANCE (gst_object_ref (element));
1420 else if (!GST_IS_BIN (element))
1423 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1424 GST_TYPE_COLOR_BALANCE);
1425 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1426 &cb) == GST_ITERATOR_RESYNC)
1427 gst_iterator_resync (it);
1428 gst_iterator_free (it);
1434 colorbalance_value_changed_cb (GstColorBalance * balance,
1435 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1440 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1441 GstColorBalanceChannel *proxy = l->data;
1443 if (g_strrstr (channel->label, proxy->label)) {
1446 /* Convert to [0, 1] range */
1449 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1450 (gdouble) channel->min_value);
1451 /* Convert to proxy range */
1453 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1454 (gdouble) proxy->min_value);
1455 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1457 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1458 playsink->colorbalance_values[i]);
1465 update_colorbalance (GstPlaySink * playsink)
1467 GstColorBalance *balance = NULL;
1471 GST_OBJECT_LOCK (playsink);
1472 if (playsink->colorbalance_element) {
1474 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1476 GST_OBJECT_UNLOCK (playsink);
1480 g_signal_handlers_block_by_func (balance,
1481 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1483 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1484 GstColorBalanceChannel *proxy = l->data;
1485 GstColorBalanceChannel *channel = NULL;
1486 const GList *channels, *k;
1488 channels = gst_color_balance_list_channels (balance);
1489 for (k = channels; k; k = k->next) {
1490 GstColorBalanceChannel *tmp = k->data;
1492 if (g_strrstr (tmp->label, proxy->label)) {
1500 gst_color_balance_set_value (balance, channel,
1501 playsink->colorbalance_values[i]);
1504 g_signal_handlers_unblock_by_func (balance,
1505 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1507 gst_object_unref (balance);
1510 /* make the element (bin) that contains the elements needed to perform
1513 * +------------------------------------------------------------+
1515 * | +-------+ +----------+ +----------+ +---------+ |
1516 * | | queue | |colorspace| |videoscale| |videosink| |
1517 * | +-sink src-sink src-sink src-sink | |
1518 * | | +-------+ +----------+ +----------+ +---------+ |
1520 * +------------------------------------------------------------+
1523 static GstPlayVideoChain *
1524 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1526 GstPlayVideoChain *chain;
1529 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1531 chain = g_new0 (GstPlayVideoChain, 1);
1532 chain->chain.playsink = playsink;
1533 chain->chain.raw = raw;
1535 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1537 if (playsink->video_sink) {
1538 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1539 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1541 /* only try fallback if no specific sink was chosen */
1542 if (chain->sink == NULL) {
1543 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1544 elem = gst_element_factory_make ("autovideosink", "videosink");
1545 chain->sink = try_element (playsink, elem, TRUE);
1547 if (chain->sink == NULL) {
1548 /* if default sink from config.h is different then try it too */
1549 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1550 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1551 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1552 chain->sink = try_element (playsink, elem, TRUE);
1556 playsink->video_sink = gst_object_ref (chain->sink);
1558 if (chain->sink == NULL)
1562 /* if we can disable async behaviour of the sink, we can avoid adding a
1563 * queue for the audio chain. */
1565 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1568 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1569 async, GST_ELEMENT_NAME (elem));
1570 g_object_set (elem, "async", async, NULL);
1571 chain->async = async;
1573 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1574 chain->async = TRUE;
1577 /* Make sure the aspect ratio is kept */
1579 gst_play_sink_find_property_sinks (playsink, chain->sink,
1580 "force-aspect-ratio", G_TYPE_BOOLEAN);
1582 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1584 /* find ts-offset element */
1585 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1586 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1589 /* create a bin to hold objects, as we create them we add them to this bin so
1590 * that when something goes wrong we only need to unref the bin */
1591 chain->chain.bin = gst_bin_new ("vbin");
1592 bin = GST_BIN_CAST (chain->chain.bin);
1593 gst_object_ref_sink (bin);
1594 gst_bin_add (bin, chain->sink);
1596 /* Get the VideoOverlay element */
1598 GstVideoOverlay *overlay = NULL;
1600 GST_OBJECT_LOCK (playsink);
1601 if (playsink->overlay_element)
1602 gst_object_unref (playsink->overlay_element);
1603 playsink->overlay_element =
1604 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1605 GST_TYPE_VIDEO_OVERLAY));
1606 if (playsink->overlay_element)
1607 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1608 GST_OBJECT_UNLOCK (playsink);
1611 if (playsink->overlay_handle_set)
1612 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1613 if (playsink->overlay_handle_events_set)
1614 gst_video_overlay_handle_events (overlay,
1615 playsink->overlay_handle_events);
1616 if (playsink->overlay_render_rectangle_set)
1617 gst_video_overlay_set_render_rectangle (overlay,
1618 playsink->overlay_x, playsink->overlay_y,
1619 playsink->overlay_width, playsink->overlay_height);
1620 gst_object_unref (overlay);
1624 /* decouple decoder from sink, this improves playback quite a lot since the
1625 * decoder can continue while the sink blocks for synchronisation. We don't
1626 * need a lot of buffers as this consumes a lot of memory and we don't want
1627 * too little because else we would be context switching too quickly. */
1628 chain->queue = gst_element_factory_make ("queue", "vqueue");
1629 if (chain->queue == NULL) {
1630 post_missing_element_message (playsink, "queue");
1631 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1632 (_("Missing element '%s' - check your GStreamer installation."),
1633 "queue"), ("video rendering might be suboptimal"));
1637 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1638 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1639 gst_bin_add (bin, chain->queue);
1640 head = prev = chain->queue;
1643 GST_OBJECT_LOCK (playsink);
1644 if (playsink->colorbalance_element) {
1645 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1646 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1647 gst_object_unref (playsink->colorbalance_element);
1649 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1650 GST_OBJECT_UNLOCK (playsink);
1652 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1653 || (!playsink->colorbalance_element
1654 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1655 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1656 gboolean use_balance = !playsink->colorbalance_element
1657 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1659 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1661 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1662 "use-converters", use_converters, "use-balance", use_balance, NULL);
1664 GST_OBJECT_LOCK (playsink);
1665 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1666 playsink->colorbalance_element =
1667 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1668 (chain->conv)->balance));
1669 GST_OBJECT_UNLOCK (playsink);
1671 gst_bin_add (bin, chain->conv);
1673 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1674 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1682 update_colorbalance (playsink);
1685 GST_DEBUG_OBJECT (playsink, "linking to sink");
1686 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1687 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1691 pad = gst_element_get_static_pad (head, "sink");
1692 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1693 gst_object_unref (pad);
1695 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1702 if (!elem && !playsink->video_sink) {
1703 post_missing_element_message (playsink, "autovideosink");
1704 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1705 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1706 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1707 (_("Both autovideosink and %s elements are missing."),
1708 DEFAULT_VIDEOSINK), (NULL));
1710 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1711 (_("The autovideosink element is missing.")), (NULL));
1714 if (playsink->video_sink) {
1715 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1716 (_("Configured videosink %s is not working."),
1717 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1718 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1719 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1720 (_("Both autovideosink and %s elements are not working."),
1721 DEFAULT_VIDEOSINK), (NULL));
1723 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1724 (_("The autovideosink element is not working.")), (NULL));
1727 free_chain ((GstPlayChain *) chain);
1733 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1734 (NULL), ("Failed to configure the video sink."));
1735 /* checking sink made it READY */
1736 gst_element_set_state (chain->sink, GST_STATE_NULL);
1737 /* Remove chain from the bin to allow reuse later */
1738 gst_bin_remove (bin, chain->sink);
1739 free_chain ((GstPlayChain *) chain);
1745 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1748 GstPlayVideoChain *chain;
1749 GstStateChangeReturn ret;
1751 chain = playsink->videochain;
1753 chain->chain.raw = raw;
1755 /* if the chain was active we don't do anything */
1756 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1759 /* try to set the sink element to READY again */
1760 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1761 if (ret == GST_STATE_CHANGE_FAILURE)
1764 /* Get the VideoOverlay element */
1766 GstVideoOverlay *overlay = NULL;
1768 GST_OBJECT_LOCK (playsink);
1769 if (playsink->overlay_element)
1770 gst_object_unref (playsink->overlay_element);
1771 playsink->overlay_element =
1772 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1773 GST_TYPE_VIDEO_OVERLAY));
1774 if (playsink->overlay_element)
1775 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1776 GST_OBJECT_UNLOCK (playsink);
1779 if (playsink->overlay_handle_set)
1780 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1781 if (playsink->overlay_handle_events_set)
1782 gst_video_overlay_handle_events (overlay,
1783 playsink->overlay_handle_events);
1784 if (playsink->overlay_render_rectangle_set)
1785 gst_video_overlay_set_render_rectangle (overlay,
1786 playsink->overlay_x, playsink->overlay_y,
1787 playsink->overlay_width, playsink->overlay_height);
1788 gst_object_unref (overlay);
1792 /* find ts-offset element */
1793 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1794 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1797 /* if we can disable async behaviour of the sink, we can avoid adding a
1798 * queue for the audio chain. */
1800 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1803 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1804 async, GST_ELEMENT_NAME (elem));
1805 g_object_set (elem, "async", async, NULL);
1806 chain->async = async;
1808 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1809 chain->async = TRUE;
1812 /* Make sure the aspect ratio is kept */
1814 gst_play_sink_find_property_sinks (playsink, chain->sink,
1815 "force-aspect-ratio", G_TYPE_BOOLEAN);
1817 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1819 GST_OBJECT_LOCK (playsink);
1820 if (playsink->colorbalance_element) {
1821 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1822 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1823 gst_object_unref (playsink->colorbalance_element);
1825 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1826 GST_OBJECT_UNLOCK (playsink);
1829 gboolean use_balance = !playsink->colorbalance_element
1830 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1832 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1834 GST_OBJECT_LOCK (playsink);
1835 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1836 playsink->colorbalance_element =
1837 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1838 (chain->conv)->balance));
1839 GST_OBJECT_UNLOCK (playsink);
1842 update_colorbalance (playsink);
1848 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1852 GstStructure *structure;
1853 event = gst_event_new_segment (segment);
1854 structure = gst_event_writable_structure (event);
1855 gst_structure_id_set (structure,
1856 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1861 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
1864 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1866 const GstStructure *structure = gst_event_get_structure (event);
1868 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB &&
1870 && strcmp (gst_structure_get_name (structure),
1871 "subtitleoverlay-flush-subtitle") == 0) {
1872 GST_DEBUG_OBJECT (pad,
1873 "Custom subtitle flush event received, marking to flush text");
1874 GST_PLAY_SINK_LOCK (playsink);
1875 playsink->ignore_wrong_state = TRUE;
1876 playsink->custom_flush_finished = FALSE;
1877 GST_PLAY_SINK_UNLOCK (playsink);
1878 } else if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB &&
1879 structure && strcmp (gst_structure_get_name (structure),
1880 "subtitleoverlay-flush-subtitle-finish") == 0) {
1881 GST_DEBUG_OBJECT (pad, "Custom subtitle flush finish event received");
1882 GST_PLAY_SINK_LOCK (playsink);
1883 playsink->pending_flush_stop = TRUE;
1884 playsink->custom_flush_finished = TRUE;
1885 GST_PLAY_SINK_UNLOCK (playsink);
1886 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1887 GST_DEBUG_OBJECT (pad,
1888 "Resetting text segment because of flush-stop event");
1889 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
1892 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
1894 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1895 const GstSegment *segment;
1897 gst_event_parse_segment (event, &segment);
1898 GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1900 if (playsink->text_segment.format != segment->format) {
1901 GST_DEBUG_OBJECT (pad, "Text segment format changed: %s -> %s",
1902 gst_format_get_name (playsink->text_segment.format),
1903 gst_format_get_name (segment->format));
1906 GST_DEBUG_OBJECT (pad, "Old text segment: %" GST_SEGMENT_FORMAT,
1907 &playsink->text_segment);
1908 gst_segment_copy_into (segment, &playsink->text_segment);
1909 GST_DEBUG_OBJECT (pad, "New text segment: %" GST_SEGMENT_FORMAT,
1910 &playsink->text_segment);
1913 gst_event_unref (event);
1914 gst_object_unref (playsink);
1918 static GstFlowReturn
1919 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
1922 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
1923 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
1926 GST_PLAY_SINK_LOCK (playsink);
1928 if (playsink->pending_flush_stop) {
1929 GstSegment text_segment;
1931 GstStructure *structure;
1933 /* it will be replaced in flush_stop */
1934 text_segment = playsink->text_segment;
1936 /* make queue drop all cached data.
1937 * This event will be dropped on the src pad. */
1938 event = gst_event_new_flush_stop (TRUE);
1939 structure = gst_event_writable_structure (event);
1940 gst_structure_id_set (structure,
1941 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1943 GST_DEBUG_OBJECT (pad,
1944 "Pushing flush-stop event with reset segment marker set: %"
1945 GST_PTR_FORMAT, event);
1946 gst_pad_send_event (pad, event);
1948 /* Re-sync queue segment info after flush-stop.
1949 * This event will be dropped on the src pad. */
1950 if (text_segment.format != GST_FORMAT_UNDEFINED) {
1953 _generate_update_newsegment_event (pad, &text_segment, &event1);
1954 GST_DEBUG_OBJECT (playsink,
1955 "Pushing segment event with reset "
1956 "segment marker set: %" GST_PTR_FORMAT, event1);
1957 gst_pad_send_event (pad, event1);
1960 playsink->pending_flush_stop = FALSE;
1962 GST_PLAY_SINK_UNLOCK (playsink);
1964 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
1966 GST_PLAY_SINK_LOCK (playsink);
1967 if (ret == GST_FLOW_FLUSHING && playsink->ignore_wrong_state) {
1968 GST_DEBUG_OBJECT (pad, "Ignoring wrong state during flush");
1969 if (playsink->custom_flush_finished) {
1970 GST_DEBUG_OBJECT (pad,
1971 "custom flush finished, stop ignoring wrong state");
1972 playsink->ignore_wrong_state = FALSE;
1977 GST_PLAY_SINK_UNLOCK (playsink);
1979 gst_object_unref (playsink);
1980 gst_object_unref (tbin);
1985 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
1989 const GstStructure *structure;
1991 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
1993 structure = gst_event_get_structure (event);
1996 gst_structure_id_has_field (structure,
1997 _playsink_reset_segment_event_marker_id)) {
1998 /* the events marked with a reset segment marker
1999 * are sent internally to reset the queue and
2000 * must be dropped here */
2001 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2002 "segment marker set: %" GST_PTR_FORMAT, event);
2007 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
2010 gst_event_unref (event);
2014 /* make an element for playback of video with subtitles embedded.
2015 * Only used for *raw* video streams.
2017 * +--------------------------------------------+
2019 * | +--------+ +-----------------+ |
2020 * | | queue | | subtitleoverlay | |
2021 * video--src sink---video_sink | |
2022 * | +--------+ | src--src
2023 * text------------------text_sink | |
2024 * | +-----------------+ |
2025 * +--------------------------------------------+
2028 static GstPlayTextChain *
2029 gen_text_chain (GstPlaySink * playsink)
2031 GstPlayTextChain *chain;
2034 GstPad *videosinkpad, *textsinkpad, *srcpad;
2036 chain = g_new0 (GstPlayTextChain, 1);
2037 chain->chain.playsink = playsink;
2039 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2041 chain->chain.bin = gst_bin_new ("tbin");
2042 bin = GST_BIN_CAST (chain->chain.bin);
2043 gst_object_ref_sink (bin);
2045 videosinkpad = textsinkpad = srcpad = NULL;
2047 /* first try to hook the text pad to the custom sink */
2048 if (playsink->text_sink) {
2049 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2050 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2053 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2056 /* make sure the sparse subtitles don't participate in the preroll */
2057 g_object_set (elem, "async", FALSE, NULL);
2058 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2059 gst_bin_add (bin, chain->sink);
2060 /* NOTE streamsynchronizer needs streams decoupled */
2061 /* make a little queue */
2062 chain->queue = gst_element_factory_make ("queue", "subqueue");
2063 if (chain->queue == NULL) {
2064 post_missing_element_message (playsink, "queue");
2065 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2066 (_("Missing element '%s' - check your GStreamer installation."),
2067 "queue"), ("rendering might be suboptimal"));
2069 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2070 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2071 "silent", TRUE, NULL);
2072 gst_bin_add (bin, chain->queue);
2074 /* we have a custom sink, this will be our textsinkpad */
2075 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2076 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2077 /* we're all fine now and we can add the sink to the chain */
2078 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2079 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2081 GST_WARNING_OBJECT (playsink,
2082 "can't find a sink pad on custom text sink");
2083 gst_bin_remove (bin, chain->sink);
2084 gst_bin_remove (bin, chain->queue);
2086 chain->queue = NULL;
2088 /* try to set sync to true but it's no biggie when we can't */
2089 if (chain->sink && (elem =
2090 gst_play_sink_find_property_sinks (playsink, chain->sink,
2091 "sync", G_TYPE_BOOLEAN)))
2092 g_object_set (elem, "sync", TRUE, NULL);
2095 gst_bin_remove (bin, chain->sink);
2097 GST_WARNING_OBJECT (playsink,
2098 "can't find async property in custom text sink");
2101 if (textsinkpad == NULL) {
2102 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2103 (_("Custom text sink element is not usable.")),
2104 ("fallback to default subtitleoverlay"));
2108 if (textsinkpad == NULL) {
2109 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2110 /* make a little queue */
2111 chain->queue = gst_element_factory_make ("queue", "vqueue");
2112 if (chain->queue == NULL) {
2113 post_missing_element_message (playsink, "queue");
2114 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2115 (_("Missing element '%s' - check your GStreamer installation."),
2116 "queue"), ("video rendering might be suboptimal"));
2118 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2119 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2120 "silent", TRUE, NULL);
2121 gst_bin_add (bin, chain->queue);
2122 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2126 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2127 if (chain->overlay == NULL) {
2128 post_missing_element_message (playsink, "subtitleoverlay");
2129 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2130 (_("Missing element '%s' - check your GStreamer installation."),
2131 "subtitleoverlay"), ("subtitle rendering disabled"));
2133 GstElement *element;
2135 gst_bin_add (bin, chain->overlay);
2137 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2138 if (playsink->font_desc) {
2139 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2140 playsink->font_desc, NULL);
2142 if (playsink->subtitle_encoding) {
2143 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2144 playsink->subtitle_encoding, NULL);
2147 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2148 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2150 /* make another little queue to decouple streams */
2151 element = gst_element_factory_make ("queue", "subqueue");
2152 if (element == NULL) {
2153 post_missing_element_message (playsink, "queue");
2154 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2155 (_("Missing element '%s' - check your GStreamer installation."),
2156 "queue"), ("rendering might be suboptimal"));
2158 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2159 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2160 "silent", TRUE, NULL);
2161 gst_bin_add (bin, element);
2162 if (gst_element_link_pads_full (element, "src", chain->overlay,
2163 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2164 textsinkpad = gst_element_get_static_pad (element, "sink");
2165 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2167 gst_bin_remove (bin, chain->sink);
2168 gst_bin_remove (bin, chain->overlay);
2170 chain->overlay = NULL;
2171 gst_object_unref (videosinkpad);
2172 videosinkpad = NULL;
2179 if (videosinkpad == NULL) {
2180 /* if we still don't have a videosink, we don't have an overlay. the only
2181 * thing we can do is insert an identity and ghost the src
2183 chain->identity = gst_element_factory_make ("identity", "tidentity");
2184 if (chain->identity == NULL) {
2185 post_missing_element_message (playsink, "identity");
2186 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2187 (_("Missing element '%s' - check your GStreamer installation."),
2188 "identity"), (NULL));
2190 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2191 g_object_set (chain->identity, "silent", TRUE, NULL);
2192 gst_bin_add (bin, chain->identity);
2193 srcpad = gst_element_get_static_pad (chain->identity, "src");
2194 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2198 /* expose the ghostpads */
2200 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2201 gst_object_unref (videosinkpad);
2202 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2205 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2206 gst_object_unref (textsinkpad);
2208 gst_pad_set_event_function (chain->textsinkpad,
2209 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2210 gst_pad_set_chain_function (chain->textsinkpad,
2211 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2213 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2216 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2217 gst_object_unref (srcpad);
2219 gst_pad_set_event_function (chain->srcpad,
2220 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2222 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2229 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2233 g_object_get (object, "volume", &vol, NULL);
2234 playsink->volume = vol;
2236 g_object_notify (G_OBJECT (playsink), "volume");
2240 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2244 g_object_get (object, "mute", &mute, NULL);
2245 playsink->mute = mute;
2247 g_object_notify (G_OBJECT (playsink), "mute");
2250 /* make the chain that contains the elements needed to perform
2253 * We add a tee as the first element so that we can link the visualisation chain
2254 * to it when requested.
2256 * +-------------------------------------------------------------+
2258 * | +---------+ +----------+ +---------+ +---------+ |
2259 * | |audioconv| |audioscale| | volume | |audiosink| |
2260 * | +-srck src-sink src-sink src-sink | |
2261 * | | +---------+ +----------+ +---------+ +---------+ |
2263 * +-------------------------------------------------------------+
2265 static GstPlayAudioChain *
2266 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2268 GstPlayAudioChain *chain;
2270 gboolean have_volume;
2272 GstElement *head, *prev, *elem = NULL;
2274 chain = g_new0 (GstPlayAudioChain, 1);
2275 chain->chain.playsink = playsink;
2276 chain->chain.raw = raw;
2278 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2280 if (playsink->audio_sink) {
2281 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2282 playsink->audio_sink);
2283 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2285 /* only try fallback if no specific sink was chosen */
2286 if (chain->sink == NULL) {
2287 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2288 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2289 chain->sink = try_element (playsink, elem, TRUE);
2291 if (chain->sink == NULL) {
2292 /* if default sink from config.h is different then try it too */
2293 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2294 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2295 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2296 chain->sink = try_element (playsink, elem, TRUE);
2300 playsink->audio_sink = gst_object_ref (chain->sink);
2302 if (chain->sink == NULL)
2305 chain->chain.bin = gst_bin_new ("abin");
2306 bin = GST_BIN_CAST (chain->chain.bin);
2307 gst_object_ref_sink (bin);
2308 gst_bin_add (bin, chain->sink);
2310 /* we have to add a queue when we need to decouple for the video sink in
2311 * visualisations and for streamsynchronizer */
2312 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2313 chain->queue = gst_element_factory_make ("queue", "aqueue");
2314 if (chain->queue == NULL) {
2315 post_missing_element_message (playsink, "queue");
2316 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2317 (_("Missing element '%s' - check your GStreamer installation."),
2318 "queue"), ("audio playback and visualizations might not work"));
2322 g_object_set (chain->queue, "silent", TRUE, NULL);
2323 gst_bin_add (bin, chain->queue);
2324 prev = head = chain->queue;
2327 /* find ts-offset element */
2328 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2329 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2332 /* check if the sink, or something within the sink, has the volume property.
2333 * If it does we don't need to add a volume element. */
2335 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2338 chain->volume = elem;
2340 g_signal_connect (chain->volume, "notify::volume",
2341 G_CALLBACK (notify_volume_cb), playsink);
2343 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2345 chain->sink_volume = TRUE;
2346 /* if the sink also has a mute property we can use this as well. We'll only
2347 * use the mute property if there is a volume property. We can simulate the
2348 * mute with the volume otherwise. */
2350 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2353 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2354 g_signal_connect (chain->mute, "notify::mute",
2355 G_CALLBACK (notify_mute_cb), playsink);
2357 /* use the sink to control the volume and mute */
2358 if (playsink->volume_changed) {
2359 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2360 playsink->volume_changed = FALSE;
2362 if (playsink->mute_changed) {
2364 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2367 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2369 playsink->mute_changed = FALSE;
2372 /* no volume, we need to add a volume element when we can */
2373 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2374 have_volume = FALSE;
2375 chain->sink_volume = FALSE;
2378 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2379 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2380 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2381 gboolean use_volume =
2382 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2383 GST_DEBUG_OBJECT (playsink,
2384 "creating audioconvert with use-converters %d, use-volume %d",
2385 use_converters, use_volume);
2387 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2388 "use-converters", use_converters, "use-volume", use_volume, NULL);
2389 gst_bin_add (bin, chain->conv);
2391 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2392 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2399 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2400 GstPlaySinkAudioConvert *conv =
2401 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2404 chain->volume = conv->volume;
2407 g_signal_connect (chain->volume, "notify::volume",
2408 G_CALLBACK (notify_volume_cb), playsink);
2410 /* volume also has the mute property */
2411 chain->mute = chain->volume;
2412 g_signal_connect (chain->mute, "notify::mute",
2413 G_CALLBACK (notify_mute_cb), playsink);
2415 /* configure with the latest volume and mute */
2416 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2418 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2424 /* we only have to link to the previous element if we have something in
2425 * front of the sink */
2426 GST_DEBUG_OBJECT (playsink, "linking to sink");
2427 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2428 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2432 /* post a warning if we have no way to configure the volume */
2434 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2435 (_("No volume control found")), ("Volume/mute is not available"));
2438 /* and ghost the sinkpad of the headmost element */
2439 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2440 pad = gst_element_get_static_pad (head, "sink");
2441 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2442 gst_object_unref (pad);
2443 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2450 if (!elem && !playsink->audio_sink) {
2451 post_missing_element_message (playsink, "autoaudiosink");
2452 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2453 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2454 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2455 (_("Both autoaudiosink and %s elements are missing."),
2456 DEFAULT_AUDIOSINK), (NULL));
2458 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2459 (_("The autoaudiosink element is missing.")), (NULL));
2462 if (playsink->audio_sink) {
2463 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2464 (_("Configured audiosink %s is not working."),
2465 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2466 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2467 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2468 (_("Both autoaudiosink and %s elements are not working."),
2469 DEFAULT_AUDIOSINK), (NULL));
2471 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2472 (_("The autoaudiosink element is not working.")), (NULL));
2475 free_chain ((GstPlayChain *) chain);
2480 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2481 (NULL), ("Failed to configure the audio sink."));
2482 /* checking sink made it READY */
2483 gst_element_set_state (chain->sink, GST_STATE_NULL);
2484 /* Remove chain from the bin to allow reuse later */
2485 gst_bin_remove (bin, chain->sink);
2486 free_chain ((GstPlayChain *) chain);
2492 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2495 GstPlayAudioChain *chain;
2496 GstStateChangeReturn ret;
2497 GstPlaySinkAudioConvert *conv;
2499 chain = playsink->audiochain;
2500 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2502 chain->chain.raw = raw;
2504 /* if the chain was active we don't do anything */
2505 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2508 /* try to set the sink element to READY again */
2509 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2510 if (ret == GST_STATE_CHANGE_FAILURE)
2513 /* find ts-offset element */
2514 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2515 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2518 /* check if the sink, or something within the sink, has the volume property.
2519 * If it does we don't need to add a volume element. */
2521 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2524 chain->volume = elem;
2526 if (playsink->volume_changed) {
2527 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2529 /* use the sink to control the volume */
2530 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2531 playsink->volume_changed = FALSE;
2534 g_signal_connect (chain->volume, "notify::volume",
2535 G_CALLBACK (notify_volume_cb), playsink);
2536 /* if the sink also has a mute property we can use this as well. We'll only
2537 * use the mute property if there is a volume property. We can simulate the
2538 * mute with the volume otherwise. */
2540 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2543 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2544 g_signal_connect (chain->mute, "notify::mute",
2545 G_CALLBACK (notify_mute_cb), playsink);
2548 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2550 /* no volume, we need to add a volume element when we can */
2551 g_object_set (chain->conv, "use-volume",
2552 !!(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2553 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2555 /* Disconnect signals */
2556 disconnect_chain (chain, playsink);
2558 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2559 chain->volume = conv->volume;
2560 chain->mute = chain->volume;
2562 g_signal_connect (chain->volume, "notify::volume",
2563 G_CALLBACK (notify_volume_cb), playsink);
2565 g_signal_connect (chain->mute, "notify::mute",
2566 G_CALLBACK (notify_mute_cb), playsink);
2568 /* configure with the latest volume and mute */
2569 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2570 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2573 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2579 * +-------------------------------------------------------------------+
2581 * | +----------+ +------------+ +----------+ +-------+ |
2582 * | | visqueue | | audioconv | | audiores | | vis | |
2583 * | +-sink src-sink + samp src-sink src-sink src-+ |
2584 * | | +----------+ +------------+ +----------+ +-------+ | |
2586 * +-------------------------------------------------------------------+
2589 static GstPlayVisChain *
2590 gen_vis_chain (GstPlaySink * playsink)
2592 GstPlayVisChain *chain;
2598 chain = g_new0 (GstPlayVisChain, 1);
2599 chain->chain.playsink = playsink;
2601 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2603 chain->chain.bin = gst_bin_new ("visbin");
2604 bin = GST_BIN_CAST (chain->chain.bin);
2605 gst_object_ref_sink (bin);
2607 /* we're queuing raw audio here, we can remove this queue when we can disable
2608 * async behaviour in the video sink. */
2609 chain->queue = gst_element_factory_make ("queue", "visqueue");
2610 if (chain->queue == NULL)
2612 g_object_set (chain->queue, "silent", TRUE, NULL);
2613 gst_bin_add (bin, chain->queue);
2615 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2616 if (chain->conv == NULL)
2617 goto no_audioconvert;
2618 gst_bin_add (bin, chain->conv);
2620 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2621 if (chain->resample == NULL)
2622 goto no_audioresample;
2623 gst_bin_add (bin, chain->resample);
2625 /* this pad will be used for blocking the dataflow and switching the vis
2627 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2629 if (playsink->visualisation) {
2630 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2631 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2633 if (chain->vis == NULL) {
2634 GST_DEBUG_OBJECT (playsink, "trying goom");
2635 elem = gst_element_factory_make ("goom", "vis");
2636 chain->vis = try_element (playsink, elem, TRUE);
2638 if (chain->vis == NULL)
2641 gst_bin_add (bin, chain->vis);
2643 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2644 GST_PAD_LINK_CHECK_NOTHING);
2646 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2647 GST_PAD_LINK_CHECK_NOTHING);
2649 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2650 GST_PAD_LINK_CHECK_NOTHING);
2654 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2655 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2657 pad = gst_element_get_static_pad (chain->queue, "sink");
2658 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2659 gst_object_unref (pad);
2660 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2662 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2663 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2670 post_missing_element_message (playsink, "queue");
2671 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2672 (_("Missing element '%s' - check your GStreamer installation."),
2674 free_chain ((GstPlayChain *) chain);
2679 post_missing_element_message (playsink, "audioconvert");
2680 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2681 (_("Missing element '%s' - check your GStreamer installation."),
2682 "audioconvert"), ("possibly a liboil version mismatch?"));
2683 free_chain ((GstPlayChain *) chain);
2688 post_missing_element_message (playsink, "audioresample");
2689 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2690 (_("Missing element '%s' - check your GStreamer installation."),
2691 "audioresample"), (NULL));
2692 free_chain ((GstPlayChain *) chain);
2697 post_missing_element_message (playsink, "goom");
2698 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2699 (_("Missing element '%s' - check your GStreamer installation."),
2701 free_chain ((GstPlayChain *) chain);
2706 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2707 (NULL), ("Failed to configure the visualisation element."));
2708 /* element made it to READY */
2709 gst_element_set_state (chain->vis, GST_STATE_NULL);
2710 free_chain ((GstPlayChain *) chain);
2715 /* this function is called when all the request pads are requested and when we
2716 * have to construct the final pipeline. Based on the flags we construct the
2717 * final output pipelines.
2720 gst_play_sink_reconfigure (GstPlaySink * playsink)
2723 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2725 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2727 /* assume we need nothing */
2728 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2730 GST_PLAY_SINK_LOCK (playsink);
2731 GST_OBJECT_LOCK (playsink);
2732 /* get flags, there are protected with the object lock */
2733 flags = playsink->flags;
2734 GST_OBJECT_UNLOCK (playsink);
2736 /* figure out which components we need */
2737 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2738 /* we have subtitles and we are requested to show it */
2742 GST_OBJECT_LOCK (playsink);
2743 if (playsink->overlay_element)
2744 gst_object_unref (playsink->overlay_element);
2745 playsink->overlay_element = NULL;
2747 if (playsink->colorbalance_element) {
2748 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2749 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2750 gst_object_unref (playsink->colorbalance_element);
2752 playsink->colorbalance_element = NULL;
2753 GST_OBJECT_UNLOCK (playsink);
2755 if (((flags & GST_PLAY_FLAG_VIDEO)
2756 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2757 /* we have video and we are requested to show it */
2760 /* we only deinterlace if native video is not requested and
2761 * we have raw video */
2762 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2763 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2764 need_deinterlace = TRUE;
2767 if (playsink->audio_pad) {
2768 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2771 if (playsink->audio_pad_raw) {
2772 /* only can do vis with raw uncompressed audio */
2773 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2774 /* also add video when we add visualisation */
2781 /* we have a text_pad and we need text rendering, in this case we need a
2782 * video_pad to combine the video with the text or visualizations */
2783 if (need_text && !need_video && !playsink->text_sink) {
2784 if (playsink->video_pad) {
2786 } else if (need_audio) {
2787 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2788 (_("Can't play a text file without video or visualizations.")),
2789 ("Have text pad but no video pad or visualizations"));
2792 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2793 (_("Can't play a text file without video or visualizations.")),
2794 ("Have text pad but no video pad or visualizations"));
2795 GST_PLAY_SINK_UNLOCK (playsink);
2800 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2801 need_video, need_vis, need_text);
2803 /* set up video pipeline */
2805 gboolean raw, async;
2807 /* we need a raw sink when we do vis or when we have a raw pad */
2808 raw = need_vis ? TRUE : playsink->video_pad_raw;
2809 /* we try to set the sink async=FALSE when we need vis, this way we can
2810 * avoid a queue in the audio chain. */
2813 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2814 playsink->video_pad_raw);
2816 if (playsink->videochain) {
2817 /* try to reactivate the chain */
2818 if (!setup_video_chain (playsink, raw, async)) {
2819 if (playsink->video_sinkpad_stream_synchronizer) {
2820 gst_element_release_request_pad (GST_ELEMENT_CAST
2821 (playsink->stream_synchronizer),
2822 playsink->video_sinkpad_stream_synchronizer);
2823 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2824 playsink->video_sinkpad_stream_synchronizer = NULL;
2825 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2826 playsink->video_srcpad_stream_synchronizer = NULL;
2829 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2831 /* Remove the sink from the bin to keep its state
2832 * and unparent it to allow reuse */
2833 if (playsink->videochain->sink)
2834 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2835 playsink->videochain->sink);
2837 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2838 free_chain ((GstPlayChain *) playsink->videochain);
2839 playsink->videochain = NULL;
2843 if (!playsink->videochain)
2844 playsink->videochain = gen_video_chain (playsink, raw, async);
2845 if (!playsink->videochain)
2848 if (!playsink->video_sinkpad_stream_synchronizer) {
2849 GValue item = { 0, };
2852 playsink->video_sinkpad_stream_synchronizer =
2853 gst_element_get_request_pad (GST_ELEMENT_CAST
2854 (playsink->stream_synchronizer), "sink_%u");
2855 it = gst_pad_iterate_internal_links
2856 (playsink->video_sinkpad_stream_synchronizer);
2858 gst_iterator_next (it, &item);
2859 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2860 g_value_unset (&item);
2861 g_assert (playsink->video_srcpad_stream_synchronizer);
2862 gst_iterator_free (it);
2865 if (playsink->video_pad)
2866 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2867 playsink->video_sinkpad_stream_synchronizer);
2869 if (need_deinterlace) {
2870 if (!playsink->videodeinterlacechain)
2871 playsink->videodeinterlacechain =
2872 gen_video_deinterlace_chain (playsink);
2873 if (!playsink->videodeinterlacechain)
2876 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2878 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2880 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2881 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2883 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2884 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2886 if (playsink->videodeinterlacechain) {
2887 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2888 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2893 GST_DEBUG_OBJECT (playsink, "adding video chain");
2894 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2895 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2896 /* if we are not part of vis or subtitles, set the ghostpad target */
2897 if (!need_vis && !need_text && (!playsink->textchain
2898 || !playsink->text_pad)) {
2899 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2900 if (need_deinterlace)
2901 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2902 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2904 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2905 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2908 GST_DEBUG_OBJECT (playsink, "no video needed");
2909 if (playsink->videochain) {
2910 GST_DEBUG_OBJECT (playsink, "removing video chain");
2911 if (playsink->vischain) {
2914 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2916 /* also had visualisation, release the tee srcpad before we then
2917 * unlink the video from it */
2918 if (playsink->audio_tee_vissrc) {
2919 gst_element_release_request_pad (playsink->audio_tee,
2920 playsink->audio_tee_vissrc);
2921 gst_object_unref (playsink->audio_tee_vissrc);
2922 playsink->audio_tee_vissrc = NULL;
2925 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2926 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2929 if (playsink->video_sinkpad_stream_synchronizer) {
2930 gst_element_release_request_pad (GST_ELEMENT_CAST
2931 (playsink->stream_synchronizer),
2932 playsink->video_sinkpad_stream_synchronizer);
2933 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2934 playsink->video_sinkpad_stream_synchronizer = NULL;
2935 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2936 playsink->video_srcpad_stream_synchronizer = NULL;
2939 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2940 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2941 if (playsink->videochain->ts_offset)
2942 gst_object_unref (playsink->videochain->ts_offset);
2943 playsink->videochain->ts_offset = NULL;
2946 if (playsink->videodeinterlacechain) {
2947 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2948 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2951 if (playsink->video_pad)
2952 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2958 GST_DEBUG_OBJECT (playsink, "adding audio");
2960 /* get a raw sink if we are asked for a raw pad */
2961 raw = playsink->audio_pad_raw;
2963 if (playsink->audiochain) {
2964 /* try to reactivate the chain */
2965 if (!setup_audio_chain (playsink, raw)) {
2966 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2967 if (playsink->audio_tee_asrc) {
2968 gst_element_release_request_pad (playsink->audio_tee,
2969 playsink->audio_tee_asrc);
2970 gst_object_unref (playsink->audio_tee_asrc);
2971 playsink->audio_tee_asrc = NULL;
2974 if (playsink->audio_sinkpad_stream_synchronizer) {
2975 gst_element_release_request_pad (GST_ELEMENT_CAST
2976 (playsink->stream_synchronizer),
2977 playsink->audio_sinkpad_stream_synchronizer);
2978 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2979 playsink->audio_sinkpad_stream_synchronizer = NULL;
2980 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2981 playsink->audio_srcpad_stream_synchronizer = NULL;
2984 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2986 /* Remove the sink from the bin to keep its state
2987 * and unparent it to allow reuse */
2988 if (playsink->audiochain->sink)
2989 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2990 playsink->audiochain->sink);
2992 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2993 disconnect_chain (playsink->audiochain, playsink);
2994 playsink->audiochain->volume = NULL;
2995 playsink->audiochain->mute = NULL;
2996 if (playsink->audiochain->ts_offset)
2997 gst_object_unref (playsink->audiochain->ts_offset);
2998 playsink->audiochain->ts_offset = NULL;
2999 free_chain ((GstPlayChain *) playsink->audiochain);
3000 playsink->audiochain = NULL;
3001 playsink->volume_changed = playsink->mute_changed = FALSE;
3005 if (!playsink->audiochain) {
3006 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3007 playsink->audiochain = gen_audio_chain (playsink, raw);
3010 if (!playsink->audio_sinkpad_stream_synchronizer) {
3011 GValue item = { 0, };
3014 playsink->audio_sinkpad_stream_synchronizer =
3015 gst_element_get_request_pad (GST_ELEMENT_CAST
3016 (playsink->stream_synchronizer), "sink_%u");
3017 it = gst_pad_iterate_internal_links
3018 (playsink->audio_sinkpad_stream_synchronizer);
3020 gst_iterator_next (it, &item);
3021 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3022 g_value_unset (&item);
3023 g_assert (playsink->audio_srcpad_stream_synchronizer);
3024 gst_iterator_free (it);
3027 if (playsink->audiochain) {
3028 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3029 if (playsink->audio_tee_asrc == NULL) {
3030 playsink->audio_tee_asrc =
3031 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3033 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3034 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3035 gst_pad_link_full (playsink->audio_tee_asrc,
3036 playsink->audio_sinkpad_stream_synchronizer,
3037 GST_PAD_LINK_CHECK_NOTHING);
3038 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3039 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3042 GST_DEBUG_OBJECT (playsink, "no audio needed");
3043 /* we have no audio or we are requested to not play audio */
3044 if (playsink->audiochain) {
3045 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3046 /* release the audio pad */
3047 if (playsink->audio_tee_asrc) {
3048 gst_element_release_request_pad (playsink->audio_tee,
3049 playsink->audio_tee_asrc);
3050 gst_object_unref (playsink->audio_tee_asrc);
3051 playsink->audio_tee_asrc = NULL;
3054 if (playsink->audio_sinkpad_stream_synchronizer) {
3055 gst_element_release_request_pad (GST_ELEMENT_CAST
3056 (playsink->stream_synchronizer),
3057 playsink->audio_sinkpad_stream_synchronizer);
3058 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3059 playsink->audio_sinkpad_stream_synchronizer = NULL;
3060 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3061 playsink->audio_srcpad_stream_synchronizer = NULL;
3064 if (playsink->audiochain->sink_volume) {
3065 disconnect_chain (playsink->audiochain, playsink);
3066 playsink->audiochain->volume = NULL;
3067 playsink->audiochain->mute = NULL;
3068 if (playsink->audiochain->ts_offset)
3069 gst_object_unref (playsink->audiochain->ts_offset);
3070 playsink->audiochain->ts_offset = NULL;
3072 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3073 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3080 if (!playsink->vischain)
3081 playsink->vischain = gen_vis_chain (playsink);
3083 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3085 if (playsink->vischain) {
3086 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3088 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3089 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3090 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3091 if (playsink->audio_tee_vissrc == NULL) {
3092 playsink->audio_tee_vissrc =
3093 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3095 gst_pad_link_full (playsink->audio_tee_vissrc,
3096 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3097 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3098 GST_PAD_LINK_CHECK_NOTHING);
3099 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3100 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3101 gst_object_unref (srcpad);
3104 GST_DEBUG_OBJECT (playsink, "no vis needed");
3105 if (playsink->vischain) {
3106 if (playsink->audio_tee_vissrc) {
3107 gst_element_release_request_pad (playsink->audio_tee,
3108 playsink->audio_tee_vissrc);
3109 gst_object_unref (playsink->audio_tee_vissrc);
3110 playsink->audio_tee_vissrc = NULL;
3112 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3113 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3114 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3119 GST_DEBUG_OBJECT (playsink, "adding text");
3120 if (!playsink->textchain) {
3121 GST_DEBUG_OBJECT (playsink, "creating text chain");
3122 playsink->textchain = gen_text_chain (playsink);
3124 if (playsink->textchain) {
3127 GST_DEBUG_OBJECT (playsink, "adding text chain");
3128 if (playsink->textchain->overlay)
3129 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3131 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3133 if (!playsink->text_sinkpad_stream_synchronizer) {
3134 GValue item = { 0, };
3136 playsink->text_sinkpad_stream_synchronizer =
3137 gst_element_get_request_pad (GST_ELEMENT_CAST
3138 (playsink->stream_synchronizer), "sink_%u");
3139 it = gst_pad_iterate_internal_links
3140 (playsink->text_sinkpad_stream_synchronizer);
3142 gst_iterator_next (it, &item);
3143 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3144 g_value_unset (&item);
3145 g_assert (playsink->text_srcpad_stream_synchronizer);
3146 gst_iterator_free (it);
3148 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3149 playsink->text_sinkpad_stream_synchronizer);
3150 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3151 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3154 if (need_vis || need_video) {
3159 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3160 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3161 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3162 GST_PAD_LINK_CHECK_NOTHING);
3163 gst_object_unref (srcpad);
3165 if (need_deinterlace)
3166 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3167 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3169 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3170 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3172 gst_pad_link_full (playsink->textchain->srcpad,
3173 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3176 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3179 GST_DEBUG_OBJECT (playsink, "no text needed");
3180 /* we have no subtitles/text or we are requested to not show them */
3182 if (playsink->text_sinkpad_stream_synchronizer) {
3183 gst_element_release_request_pad (GST_ELEMENT_CAST
3184 (playsink->stream_synchronizer),
3185 playsink->text_sinkpad_stream_synchronizer);
3186 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3187 playsink->text_sinkpad_stream_synchronizer = NULL;
3188 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3189 playsink->text_srcpad_stream_synchronizer = NULL;
3192 if (playsink->textchain) {
3193 if (playsink->text_pad == NULL) {
3194 /* no text pad, remove the chain entirely */
3195 GST_DEBUG_OBJECT (playsink, "removing text chain");
3196 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3197 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3199 /* we have a chain and a textpad, turn the subtitles off */
3200 GST_DEBUG_OBJECT (playsink, "turning off the text");
3201 if (playsink->textchain->overlay)
3202 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3206 if (!need_video && playsink->video_pad) {
3207 if (playsink->video_sinkpad_stream_synchronizer) {
3208 gst_element_release_request_pad (GST_ELEMENT_CAST
3209 (playsink->stream_synchronizer),
3210 playsink->video_sinkpad_stream_synchronizer);
3211 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3212 playsink->video_sinkpad_stream_synchronizer = NULL;
3213 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3214 playsink->video_srcpad_stream_synchronizer = NULL;
3217 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3220 if (playsink->text_pad && !playsink->textchain)
3221 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3223 update_av_offset (playsink);
3224 do_async_done (playsink);
3225 GST_PLAY_SINK_UNLOCK (playsink);
3232 /* gen_ chain already posted error */
3233 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3234 GST_PLAY_SINK_UNLOCK (playsink);
3240 * gst_play_sink_set_flags:
3241 * @playsink: a #GstPlaySink
3242 * @flags: #GstPlayFlags
3244 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3245 * when constructing the sink pipelins.
3247 * Returns: TRUE if the flags could be configured.
3250 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3252 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3254 GST_OBJECT_LOCK (playsink);
3255 playsink->flags = flags;
3256 GST_OBJECT_UNLOCK (playsink);
3262 * gst_play_sink_get_flags:
3263 * @playsink: a #GstPlaySink
3265 * Get the flags of @playsink. That flags control the behaviour of the sink when
3266 * it constructs the sink pipelines.
3268 * Returns: the currently configured #GstPlayFlags.
3271 gst_play_sink_get_flags (GstPlaySink * playsink)
3275 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3277 GST_OBJECT_LOCK (playsink);
3278 res = playsink->flags;
3279 GST_OBJECT_UNLOCK (playsink);
3285 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3287 GstPlayTextChain *chain;
3289 GST_PLAY_SINK_LOCK (playsink);
3290 chain = (GstPlayTextChain *) playsink->textchain;
3291 g_free (playsink->font_desc);
3292 playsink->font_desc = g_strdup (desc);
3293 if (chain && chain->overlay) {
3294 g_object_set (chain->overlay, "font-desc", desc, NULL);
3296 GST_PLAY_SINK_UNLOCK (playsink);
3300 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3302 gchar *result = NULL;
3303 GstPlayTextChain *chain;
3305 GST_PLAY_SINK_LOCK (playsink);
3306 chain = (GstPlayTextChain *) playsink->textchain;
3307 if (chain && chain->overlay) {
3308 g_object_get (chain->overlay, "font-desc", &result, NULL);
3309 playsink->font_desc = g_strdup (result);
3311 result = g_strdup (playsink->font_desc);
3313 GST_PLAY_SINK_UNLOCK (playsink);
3319 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3320 const gchar * encoding)
3322 GstPlayTextChain *chain;
3324 GST_PLAY_SINK_LOCK (playsink);
3325 chain = (GstPlayTextChain *) playsink->textchain;
3326 g_free (playsink->subtitle_encoding);
3327 playsink->subtitle_encoding = g_strdup (encoding);
3328 if (chain && chain->overlay) {
3329 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3331 GST_PLAY_SINK_UNLOCK (playsink);
3335 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3337 gchar *result = NULL;
3338 GstPlayTextChain *chain;
3340 GST_PLAY_SINK_LOCK (playsink);
3341 chain = (GstPlayTextChain *) playsink->textchain;
3342 if (chain && chain->overlay) {
3343 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3344 playsink->subtitle_encoding = g_strdup (result);
3346 result = g_strdup (playsink->subtitle_encoding);
3348 GST_PLAY_SINK_UNLOCK (playsink);
3354 update_av_offset (GstPlaySink * playsink)
3357 GstPlayAudioChain *achain;
3358 GstPlayVideoChain *vchain;
3360 av_offset = playsink->av_offset;
3361 achain = (GstPlayAudioChain *) playsink->audiochain;
3362 vchain = (GstPlayVideoChain *) playsink->videochain;
3364 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3365 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3366 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3368 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3373 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3375 GST_PLAY_SINK_LOCK (playsink);
3376 playsink->av_offset = av_offset;
3377 update_av_offset (playsink);
3378 GST_PLAY_SINK_UNLOCK (playsink);
3382 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3386 GST_PLAY_SINK_LOCK (playsink);
3387 result = playsink->av_offset;
3388 GST_PLAY_SINK_UNLOCK (playsink);
3394 * gst_play_sink_get_last_sample:
3395 * @playsink: a #GstPlaySink
3397 * Get the last displayed sample from @playsink. This sample is in the native
3398 * format of the sink element, the caps in the result sample contain the format
3399 * of the frame data.
3401 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3405 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3407 GstSample *result = NULL;
3408 GstPlayVideoChain *chain;
3410 GST_PLAY_SINK_LOCK (playsink);
3411 GST_DEBUG_OBJECT (playsink, "taking last sample");
3412 /* get the video chain if we can */
3413 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3414 GST_DEBUG_OBJECT (playsink, "found video chain");
3415 /* see if the chain is active */
3416 if (chain->chain.activated && chain->sink) {
3419 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3421 /* find and get the last-buffer property now */
3423 gst_play_sink_find_property (playsink, chain->sink,
3424 "last-sample", GST_TYPE_SAMPLE))) {
3425 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3426 g_object_get (elem, "last-sample", &result, NULL);
3427 gst_object_unref (elem);
3431 GST_PLAY_SINK_UNLOCK (playsink);
3437 * gst_play_sink_convert_sample:
3438 * @playsink: a #GstPlaySink
3441 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3442 * be in the native format of the sink element and the caps on the buffer
3443 * describe the format of the frame. If @caps is not %NULL, the video
3444 * frame will be converted to the format of the caps.
3446 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3447 * available or when the conversion failed.
3450 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3455 result = gst_play_sink_get_last_sample (playsink);
3456 if (result != NULL && caps != NULL) {
3459 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3460 if (temp == NULL && err)
3463 gst_sample_unref (result);
3471 /* I'm really uncertain whether we should make playsink post an error
3472 * on the bus or not. It's not like it's a critical issue regarding
3473 * playsink behaviour. */
3474 GST_ERROR ("Error converting frame: %s", err->message);
3475 gst_sample_unref (result);
3482 is_raw_structure (GstStructure * s)
3486 name = gst_structure_get_name (s);
3488 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3494 is_raw_pad (GstPad * pad)
3496 GstPad *peer = gst_pad_get_peer (pad);
3498 gboolean raw = TRUE;
3503 caps = gst_pad_get_current_caps (peer);
3507 caps = gst_pad_query_caps (peer, NULL);
3509 n = gst_caps_get_size (caps);
3510 for (i = 0; i < n; i++) {
3511 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3515 } else if (raw != r) {
3516 GST_ERROR_OBJECT (pad,
3517 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3523 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3525 gst_caps_unref (caps);
3526 gst_object_unref (peer);
3531 static GstPadProbeReturn
3532 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3533 gpointer user_data);
3536 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3538 if (playsink->video_pad) {
3540 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3541 (playsink->video_pad)));
3542 if (blocked && playsink->video_block_id == 0) {
3543 playsink->video_block_id =
3544 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3545 sinkpad_blocked_cb, playsink, NULL);
3546 } else if (!blocked && playsink->video_block_id) {
3547 gst_pad_remove_probe (opad, playsink->video_block_id);
3548 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3549 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3550 playsink->video_block_id = 0;
3551 playsink->video_pad_blocked = FALSE;
3553 gst_object_unref (opad);
3558 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3560 if (playsink->audio_pad) {
3562 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3563 (playsink->audio_pad)));
3564 if (blocked && playsink->audio_block_id == 0) {
3565 playsink->audio_block_id =
3566 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3567 sinkpad_blocked_cb, playsink, NULL);
3568 } else if (!blocked && playsink->audio_block_id) {
3569 gst_pad_remove_probe (opad, playsink->audio_block_id);
3570 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3571 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3572 playsink->audio_block_id = 0;
3573 playsink->audio_pad_blocked = FALSE;
3575 gst_object_unref (opad);
3580 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3582 if (playsink->text_pad) {
3584 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3585 (playsink->text_pad)));
3586 if (blocked && playsink->text_block_id == 0) {
3587 playsink->text_block_id =
3588 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3589 sinkpad_blocked_cb, playsink, NULL);
3590 } else if (!blocked && playsink->text_block_id) {
3591 gst_pad_remove_probe (opad, playsink->text_block_id);
3592 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3593 playsink->text_block_id = 0;
3594 playsink->text_pad_blocked = FALSE;
3596 gst_object_unref (opad);
3600 static GstPadProbeReturn
3601 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3604 GstPlaySink *playsink = (GstPlaySink *) user_data;
3607 GST_PLAY_SINK_LOCK (playsink);
3609 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3610 if (pad == playsink->video_pad) {
3611 playsink->video_pad_blocked = TRUE;
3612 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3613 } else if (pad == playsink->audio_pad) {
3614 playsink->audio_pad_blocked = TRUE;
3615 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3616 } else if (pad == playsink->text_pad) {
3617 playsink->text_pad_blocked = TRUE;
3618 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3621 /* We reconfigure when for ALL streams:
3622 * * there isn't a pad
3623 * * OR the pad is blocked
3624 * * OR there are no pending blocks on that pad
3627 if ((!playsink->video_pad || playsink->video_pad_blocked
3628 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3629 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3630 && (!playsink->text_pad || playsink->text_pad_blocked
3631 || !PENDING_TEXT_BLOCK (playsink))) {
3632 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3634 if (playsink->video_pad) {
3635 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3636 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3637 playsink->video_pad_raw);
3640 if (playsink->audio_pad) {
3641 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3642 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3643 playsink->audio_pad_raw);
3646 gst_play_sink_reconfigure (playsink);
3648 video_set_blocked (playsink, FALSE);
3649 audio_set_blocked (playsink, FALSE);
3650 text_set_blocked (playsink, FALSE);
3653 gst_object_unref (pad);
3655 GST_PLAY_SINK_UNLOCK (playsink);
3657 return GST_PAD_PROBE_OK;
3661 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3663 gboolean reconfigure = FALSE;
3667 g_object_get (pad, "caps", &caps, NULL);
3671 if (pad == playsink->audio_pad) {
3672 raw = is_raw_pad (pad);
3673 reconfigure = (!!playsink->audio_pad_raw != !!raw)
3674 && playsink->audiochain;
3675 GST_DEBUG_OBJECT (pad,
3676 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3678 } else if (pad == playsink->video_pad) {
3679 raw = is_raw_pad (pad);
3680 reconfigure = (!!playsink->video_pad_raw != !!raw)
3681 && playsink->videochain;
3682 GST_DEBUG_OBJECT (pad,
3683 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3687 gst_caps_unref (caps);
3690 GST_PLAY_SINK_LOCK (playsink);
3691 video_set_blocked (playsink, TRUE);
3692 audio_set_blocked (playsink, TRUE);
3693 text_set_blocked (playsink, TRUE);
3694 GST_PLAY_SINK_UNLOCK (playsink);
3699 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3700 GstPlaySinkType type)
3702 gulong *block_id = NULL;
3704 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3706 GST_PLAY_SINK_LOCK (playsink);
3707 if (pad == playsink->video_pad) {
3708 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3709 type != GST_PLAY_SINK_TYPE_VIDEO)
3711 block_id = &playsink->video_block_id;
3712 } else if (pad == playsink->audio_pad) {
3713 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3714 type != GST_PLAY_SINK_TYPE_AUDIO)
3716 block_id = &playsink->audio_block_id;
3717 } else if (pad == playsink->text_pad) {
3718 if (type != GST_PLAY_SINK_TYPE_TEXT)
3720 block_id = &playsink->text_block_id;
3723 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3725 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3728 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3729 sinkpad_blocked_cb, playsink, NULL);
3730 PENDING_FLAG_SET (playsink, type);
3731 gst_object_unref (blockpad);
3733 GST_PLAY_SINK_UNLOCK (playsink);
3740 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3742 GST_PLAY_SINK_UNLOCK (playsink);
3748 * gst_play_sink_request_pad
3749 * @playsink: a #GstPlaySink
3750 * @type: a #GstPlaySinkType
3752 * Create or return a pad of @type.
3754 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3757 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3760 gboolean created = FALSE;
3761 gboolean activate = TRUE;
3762 const gchar *pad_name = NULL;
3763 gulong *block_id = NULL;
3765 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3767 GST_PLAY_SINK_LOCK (playsink);
3769 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3770 case GST_PLAY_SINK_TYPE_AUDIO:
3771 pad_name = "audio_sink";
3772 if (!playsink->audio_tee) {
3773 GST_LOG_OBJECT (playsink, "creating tee");
3774 /* create tee when needed. This element will feed the audio sink chain
3775 * and the vis chain. */
3776 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3777 if (playsink->audio_tee == NULL) {
3778 post_missing_element_message (playsink, "tee");
3779 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3780 (_("Missing element '%s' - check your GStreamer installation."),
3785 playsink->audio_tee_sink =
3786 gst_element_get_static_pad (playsink->audio_tee, "sink");
3787 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3788 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3791 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3793 if (!playsink->audio_pad) {
3794 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3795 playsink->audio_pad =
3796 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3797 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3798 G_CALLBACK (caps_notify_cb), playsink);
3801 playsink->audio_pad_raw = FALSE;
3802 res = playsink->audio_pad;
3803 block_id = &playsink->audio_block_id;
3805 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3806 case GST_PLAY_SINK_TYPE_VIDEO:
3807 pad_name = "video_sink";
3808 if (!playsink->video_pad) {
3809 GST_LOG_OBJECT (playsink, "ghosting videosink");
3810 playsink->video_pad =
3811 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3812 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3813 G_CALLBACK (caps_notify_cb), playsink);
3816 playsink->video_pad_raw = FALSE;
3817 res = playsink->video_pad;
3818 block_id = &playsink->video_block_id;
3820 case GST_PLAY_SINK_TYPE_TEXT:
3821 GST_LOG_OBJECT (playsink, "ghosting text");
3822 if (!playsink->text_pad) {
3823 playsink->text_pad =
3824 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3827 res = playsink->text_pad;
3828 block_id = &playsink->text_block_id;
3830 case GST_PLAY_SINK_TYPE_FLUSHING:
3834 /* we need a unique padname for the flushing pad. */
3835 padname = g_strdup_printf ("flushing_%u", playsink->count);
3836 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3847 GST_PLAY_SINK_UNLOCK (playsink);
3849 if (created && res) {
3850 /* we have to add the pad when it's active or we get an error when the
3851 * element is 'running' */
3852 gst_pad_set_active (res, TRUE);
3853 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3854 if (block_id && *block_id == 0) {
3856 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3859 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3860 sinkpad_blocked_cb, playsink, NULL);
3861 PENDING_FLAG_SET (playsink, type);
3862 gst_object_unref (blockpad);
3865 gst_pad_set_active (res, activate);
3873 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3874 const gchar * name, const GstCaps * caps)
3878 GstPlaySinkType type;
3879 const gchar *tplname;
3881 g_return_val_if_fail (templ != NULL, NULL);
3883 GST_DEBUG_OBJECT (element, "name:%s", name);
3885 psink = GST_PLAY_SINK (element);
3886 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3888 /* Figure out the GstPlaySinkType based on the template */
3889 if (!strcmp (tplname, "audio_sink"))
3890 type = GST_PLAY_SINK_TYPE_AUDIO;
3891 else if (!strcmp (tplname, "audio_raw_sink"))
3892 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3893 else if (!strcmp (tplname, "video_sink"))
3894 type = GST_PLAY_SINK_TYPE_VIDEO;
3895 else if (!strcmp (tplname, "video_raw_sink"))
3896 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3897 else if (!strcmp (tplname, "text_sink"))
3898 type = GST_PLAY_SINK_TYPE_TEXT;
3900 goto unknown_template;
3902 pad = gst_play_sink_request_pad (psink, type);
3906 GST_WARNING_OBJECT (element, "Unknown pad template");
3911 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3913 GstPad **res = NULL;
3914 gboolean untarget = TRUE;
3916 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3918 GST_PLAY_SINK_LOCK (playsink);
3919 if (pad == playsink->video_pad) {
3920 res = &playsink->video_pad;
3921 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3923 } else if (pad == playsink->audio_pad) {
3924 res = &playsink->audio_pad;
3925 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3927 } else if (pad == playsink->text_pad) {
3928 res = &playsink->text_pad;
3930 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3934 GST_PLAY_SINK_UNLOCK (playsink);
3937 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3938 gst_pad_set_active (*res, FALSE);
3940 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3941 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3943 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3944 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3950 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3952 GstPlaySink *psink = GST_PLAY_SINK (element);
3954 gst_play_sink_release_pad (psink, pad);
3958 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3960 GstPlaySink *playsink;
3962 playsink = GST_PLAY_SINK_CAST (bin);
3964 switch (GST_MESSAGE_TYPE (message)) {
3965 case GST_MESSAGE_STEP_DONE:
3970 gboolean flush, intermediate, eos;
3973 GST_INFO_OBJECT (playsink, "Handling step-done message");
3974 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3975 &intermediate, &duration, &eos);
3977 if (format == GST_FORMAT_BUFFERS) {
3978 /* for the buffer format, we align the other streams */
3979 if (playsink->audiochain) {
3983 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3986 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3987 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3991 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3994 case GST_MESSAGE_ELEMENT:{
3995 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
3996 GstVideoOverlay *overlay;
3998 GST_OBJECT_LOCK (playsink);
3999 if (playsink->overlay_element
4000 && GST_OBJECT_CAST (playsink->overlay_element) !=
4001 GST_MESSAGE_SRC (message)) {
4002 gst_object_unref (playsink->overlay_element);
4003 playsink->overlay_element = NULL;
4006 if (!playsink->overlay_element)
4007 playsink->overlay_element =
4008 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4010 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4011 GST_OBJECT_UNLOCK (playsink);
4013 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4015 if (playsink->overlay_handle_set)
4016 gst_video_overlay_set_window_handle (playsink->overlay_element,
4017 playsink->overlay_handle);
4018 if (playsink->overlay_handle_events_set)
4019 gst_video_overlay_handle_events (playsink->overlay_element,
4020 playsink->overlay_handle_events);
4021 if (playsink->overlay_render_rectangle_set)
4022 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4023 playsink->overlay_x, playsink->overlay_y,
4024 playsink->overlay_width, playsink->overlay_height);
4026 gst_object_unref (overlay);
4027 gst_message_unref (message);
4028 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4030 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4036 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4041 /* Send an event to our sinks until one of them works; don't then send to the
4042 * remaining sinks (unlike GstBin)
4043 * Special case: If a text sink is set we need to send the event
4044 * to them in case it's source is different from the a/v stream's source.
4047 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4049 gboolean res = TRUE;
4050 if (playsink->send_event_mode == MODE_FIRST) {
4051 if (playsink->textchain && playsink->textchain->sink) {
4052 gst_event_ref (event);
4054 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4055 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4057 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4061 if (playsink->videochain) {
4062 gst_event_ref (event);
4064 gst_element_send_event (playsink->videochain->chain.bin,
4066 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4069 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4071 if (playsink->audiochain) {
4072 gst_event_ref (event);
4074 gst_element_send_event (playsink->audiochain->chain.bin,
4076 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4079 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4083 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4084 (GST_ELEMENT_CAST (playsink), event);
4088 gst_event_unref (event);
4092 /* We only want to send the event to a single sink (overriding GstBin's
4093 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4094 * events appropriately. So, this is a messy duplication of code. */
4096 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4098 gboolean res = FALSE;
4099 GstEventType event_type = GST_EVENT_TYPE (event);
4100 GstPlaySink *playsink;
4101 playsink = GST_PLAY_SINK_CAST (element);
4102 switch (event_type) {
4103 case GST_EVENT_SEEK:
4104 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4105 res = gst_play_sink_send_event_to_sink (playsink, event);
4107 case GST_EVENT_STEP:
4112 gboolean flush, intermediate;
4113 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4115 if (format == GST_FORMAT_BUFFERS) {
4116 /* for buffers, we will try to step video frames, for other formats we
4117 * send the step to all sinks */
4118 res = gst_play_sink_send_event_to_sink (playsink, event);
4121 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4128 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4135 static GstStateChangeReturn
4136 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4138 GstStateChangeReturn ret;
4139 GstStateChangeReturn bret;
4140 GstPlaySink *playsink;
4141 playsink = GST_PLAY_SINK (element);
4142 switch (transition) {
4143 case GST_STATE_CHANGE_READY_TO_PAUSED:
4144 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4146 playsink->need_async_start = TRUE;
4147 /* we want to go async to PAUSED until we managed to configure and add the
4149 do_async_start (playsink);
4150 ret = GST_STATE_CHANGE_ASYNC;
4152 /* block all pads here */
4153 GST_PLAY_SINK_LOCK (playsink);
4154 if (playsink->video_pad && playsink->video_block_id == 0) {
4156 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4157 (playsink->video_pad)));
4158 playsink->video_block_id =
4159 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4160 sinkpad_blocked_cb, playsink, NULL);
4161 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4162 gst_object_unref (opad);
4165 if (playsink->audio_pad && playsink->audio_block_id == 0) {
4167 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4168 (playsink->audio_pad)));
4169 playsink->audio_block_id =
4170 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4171 sinkpad_blocked_cb, playsink, NULL);
4172 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4173 gst_object_unref (opad);
4176 if (playsink->text_pad && playsink->text_block_id == 0) {
4178 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4179 (playsink->text_pad)));
4180 playsink->text_block_id =
4181 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4182 sinkpad_blocked_cb, playsink, NULL);
4183 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4184 gst_object_unref (opad);
4186 GST_PLAY_SINK_UNLOCK (playsink);
4188 case GST_STATE_CHANGE_PAUSED_TO_READY:
4189 /* unblock all pads here */
4190 GST_PLAY_SINK_LOCK (playsink);
4191 video_set_blocked (playsink, FALSE);
4192 audio_set_blocked (playsink, FALSE);
4193 text_set_blocked (playsink, FALSE);
4194 GST_PLAY_SINK_UNLOCK (playsink);
4196 case GST_STATE_CHANGE_READY_TO_NULL:
4197 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4198 /* remove our links to the mute and volume elements when they were
4199 * provided by a sink */
4200 disconnect_chain (playsink->audiochain, playsink);
4201 playsink->audiochain->volume = NULL;
4202 playsink->audiochain->mute = NULL;
4205 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4206 gst_object_unref (playsink->audiochain->ts_offset);
4207 playsink->audiochain->ts_offset = NULL;
4210 if (playsink->videochain && playsink->videochain->ts_offset) {
4211 gst_object_unref (playsink->videochain->ts_offset);
4212 playsink->videochain->ts_offset = NULL;
4215 GST_OBJECT_LOCK (playsink);
4216 if (playsink->overlay_element)
4217 gst_object_unref (playsink->overlay_element);
4218 playsink->overlay_element = NULL;
4220 if (playsink->colorbalance_element) {
4221 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4222 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4223 gst_object_unref (playsink->colorbalance_element);
4225 playsink->colorbalance_element = NULL;
4226 GST_OBJECT_UNLOCK (playsink);
4228 ret = GST_STATE_CHANGE_SUCCESS;
4231 /* all other state changes return SUCCESS by default, this value can be
4232 * overridden by the result of the children */
4233 ret = GST_STATE_CHANGE_SUCCESS;
4237 /* do the state change of the children */
4239 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4241 /* now look at the result of our children and adjust the return value */
4243 case GST_STATE_CHANGE_FAILURE:
4244 /* failure, we stop */
4245 goto activate_failed;
4246 case GST_STATE_CHANGE_NO_PREROLL:
4247 /* some child returned NO_PREROLL. This is strange but we never know. We
4248 * commit our async state change (if any) and return the NO_PREROLL */
4249 do_async_done (playsink);
4252 case GST_STATE_CHANGE_ASYNC:
4253 /* some child was async, return this */
4257 /* return our previously configured return value */
4261 switch (transition) {
4262 case GST_STATE_CHANGE_READY_TO_PAUSED:
4264 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4265 /* FIXME Release audio device when we implement that */
4266 playsink->need_async_start = TRUE;
4268 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4269 if (playsink->video_sinkpad_stream_synchronizer) {
4270 gst_element_release_request_pad (GST_ELEMENT_CAST
4271 (playsink->stream_synchronizer),
4272 playsink->video_sinkpad_stream_synchronizer);
4273 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4274 playsink->video_sinkpad_stream_synchronizer = NULL;
4275 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4276 playsink->video_srcpad_stream_synchronizer = NULL;
4278 if (playsink->audio_sinkpad_stream_synchronizer) {
4279 gst_element_release_request_pad (GST_ELEMENT_CAST
4280 (playsink->stream_synchronizer),
4281 playsink->audio_sinkpad_stream_synchronizer);
4282 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4283 playsink->audio_sinkpad_stream_synchronizer = NULL;
4284 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4285 playsink->audio_srcpad_stream_synchronizer = NULL;
4287 if (playsink->text_sinkpad_stream_synchronizer) {
4288 gst_element_release_request_pad (GST_ELEMENT_CAST
4289 (playsink->stream_synchronizer),
4290 playsink->text_sinkpad_stream_synchronizer);
4291 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4292 playsink->text_sinkpad_stream_synchronizer = NULL;
4293 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4294 playsink->text_srcpad_stream_synchronizer = NULL;
4298 case GST_STATE_CHANGE_READY_TO_NULL:
4299 /* remove sinks we added */
4300 if (playsink->videodeinterlacechain) {
4301 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4303 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4305 if (playsink->videochain) {
4306 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4307 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4309 if (playsink->audiochain) {
4310 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4311 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4313 if (playsink->vischain) {
4314 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4315 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4317 if (playsink->textchain) {
4318 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4319 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4321 do_async_done (playsink);
4322 /* when going to READY, keep elements around as long as possible,
4323 * so they may be re-used faster next time/url around.
4324 * when really going to NULL, clean up everything completely. */
4325 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4327 /* Unparent the sinks to allow reuse */
4328 if (playsink->videochain && playsink->videochain->sink)
4329 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4330 playsink->videochain->sink);
4331 if (playsink->audiochain && playsink->audiochain->sink)
4332 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4333 playsink->audiochain->sink);
4334 if (playsink->textchain && playsink->textchain->sink)
4335 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4336 playsink->textchain->sink);
4337 if (playsink->audio_sink != NULL)
4338 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4339 if (playsink->video_sink != NULL)
4340 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4341 if (playsink->visualisation != NULL)
4342 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4343 if (playsink->text_sink != NULL)
4344 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4345 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4346 playsink->videodeinterlacechain = NULL;
4347 free_chain ((GstPlayChain *) playsink->videochain);
4348 playsink->videochain = NULL;
4349 free_chain ((GstPlayChain *) playsink->audiochain);
4350 playsink->audiochain = NULL;
4351 free_chain ((GstPlayChain *) playsink->vischain);
4352 playsink->vischain = NULL;
4353 free_chain ((GstPlayChain *) playsink->textchain);
4354 playsink->textchain = NULL;
4364 GST_DEBUG_OBJECT (element,
4365 "element failed to change states -- activation problem?");
4366 return GST_STATE_CHANGE_FAILURE;
4371 gst_play_sink_set_property (GObject * object, guint prop_id,
4372 const GValue * value, GParamSpec * spec)
4374 GstPlaySink *playsink = GST_PLAY_SINK (object);
4377 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4380 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4383 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4385 case PROP_FONT_DESC:
4386 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4388 case PROP_SUBTITLE_ENCODING:
4389 gst_play_sink_set_subtitle_encoding (playsink,
4390 g_value_get_string (value));
4392 case PROP_VIS_PLUGIN:
4393 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4395 case PROP_AV_OFFSET:
4396 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4398 case PROP_VIDEO_SINK:
4399 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4400 g_value_get_object (value));
4402 case PROP_AUDIO_SINK:
4403 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4404 g_value_get_object (value));
4406 case PROP_TEXT_SINK:
4407 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4408 g_value_get_object (value));
4410 case PROP_SEND_EVENT_MODE:
4411 playsink->send_event_mode = g_value_get_enum (value);
4414 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4420 gst_play_sink_get_property (GObject * object, guint prop_id,
4421 GValue * value, GParamSpec * spec)
4423 GstPlaySink *playsink = GST_PLAY_SINK (object);
4426 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4429 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4432 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4434 case PROP_FONT_DESC:
4435 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4437 case PROP_SUBTITLE_ENCODING:
4438 g_value_take_string (value,
4439 gst_play_sink_get_subtitle_encoding (playsink));
4441 case PROP_VIS_PLUGIN:
4442 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4445 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4447 case PROP_AV_OFFSET:
4448 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4450 case PROP_VIDEO_SINK:
4451 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4452 GST_PLAY_SINK_TYPE_VIDEO));
4454 case PROP_AUDIO_SINK:
4455 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4456 GST_PLAY_SINK_TYPE_AUDIO));
4458 case PROP_TEXT_SINK:
4459 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4460 GST_PLAY_SINK_TYPE_TEXT));
4462 case PROP_SEND_EVENT_MODE:
4463 g_value_set_enum (value, playsink->send_event_mode);
4466 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4472 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4474 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4475 GstVideoOverlay *overlay_element;
4477 GST_OBJECT_LOCK (playsink);
4478 if (playsink->overlay_element)
4480 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4482 overlay_element = NULL;
4483 GST_OBJECT_UNLOCK (playsink);
4485 if (overlay_element) {
4486 gst_video_overlay_expose (overlay_element);
4487 gst_object_unref (overlay_element);
4492 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4493 gboolean handle_events)
4495 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4496 GstVideoOverlay *overlay_element;
4498 GST_OBJECT_LOCK (playsink);
4499 if (playsink->overlay_element)
4501 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4503 overlay_element = NULL;
4504 GST_OBJECT_UNLOCK (playsink);
4506 playsink->overlay_handle_events_set = TRUE;
4507 playsink->overlay_handle_events = handle_events;
4509 if (overlay_element) {
4510 gst_video_overlay_handle_events (overlay_element, handle_events);
4511 gst_object_unref (overlay_element);
4516 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4517 gint y, gint width, gint height)
4519 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4520 GstVideoOverlay *overlay_element;
4522 GST_OBJECT_LOCK (playsink);
4523 if (playsink->overlay_element)
4525 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4527 overlay_element = NULL;
4528 GST_OBJECT_UNLOCK (playsink);
4530 playsink->overlay_render_rectangle_set = TRUE;
4531 playsink->overlay_x = x;
4532 playsink->overlay_y = y;
4533 playsink->overlay_width = width;
4534 playsink->overlay_height = height;
4536 if (overlay_element) {
4537 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4539 gst_object_unref (overlay_element);
4544 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4547 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4548 GstVideoOverlay *overlay_element;
4550 GST_OBJECT_LOCK (playsink);
4551 if (playsink->overlay_element)
4553 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4555 overlay_element = NULL;
4556 GST_OBJECT_UNLOCK (playsink);
4558 playsink->overlay_handle_set = TRUE;
4559 playsink->overlay_handle = handle;
4561 if (overlay_element) {
4562 gst_video_overlay_set_window_handle (overlay_element, handle);
4563 gst_object_unref (overlay_element);
4568 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4570 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4571 iface->expose = gst_play_sink_overlay_expose;
4572 iface->handle_events = gst_play_sink_overlay_handle_events;
4573 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4574 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4578 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4579 GstStructure * structure)
4581 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4584 GST_PLAY_SINK_LOCK (playsink);
4585 if (playsink->videochain && playsink->videochain->chain.bin)
4586 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4587 GST_PLAY_SINK_UNLOCK (playsink);
4590 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4593 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4595 gst_object_unref (nav);
4597 GstEvent *event = gst_event_new_navigation (structure);
4599 gst_element_send_event (GST_ELEMENT (bin), event);
4602 gst_object_unref (bin);
4606 gst_structure_free (structure);
4610 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4612 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4614 iface->send_event = gst_play_sink_navigation_send_event;
4617 static const GList *
4618 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4620 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4622 return playsink->colorbalance_channels;
4626 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4627 GstColorBalanceChannel * proxy, gint value)
4629 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4632 GstColorBalance *balance_element = NULL;
4634 GST_OBJECT_LOCK (playsink);
4635 if (playsink->colorbalance_element)
4637 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4638 GST_OBJECT_UNLOCK (playsink);
4640 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4641 GstColorBalanceChannel *proxy_tmp = l->data;
4644 if (proxy_tmp != proxy)
4647 playsink->colorbalance_values[i] = value;
4649 if (balance_element) {
4650 GstColorBalanceChannel *channel = NULL;
4651 const GList *channels, *k;
4653 channels = gst_color_balance_list_channels (balance_element);
4654 for (k = channels; k; k = k->next) {
4655 GstColorBalanceChannel *tmp = l->data;
4657 if (g_strrstr (tmp->label, proxy->label)) {
4665 /* Convert to [0, 1] range */
4668 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4669 (gdouble) proxy->min_value);
4670 /* Convert to channel range */
4672 channel->min_value + new_val * ((gdouble) channel->max_value -
4673 (gdouble) channel->min_value);
4675 gst_color_balance_set_value (balance_element, channel,
4676 (gint) (new_val + 0.5));
4678 gst_object_unref (balance_element);
4681 gst_color_balance_value_changed (balance, proxy, value);
4687 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4688 GstColorBalanceChannel * proxy)
4690 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4694 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4695 GstColorBalanceChannel *proxy_tmp = l->data;
4697 if (proxy_tmp != proxy)
4700 return playsink->colorbalance_values[i];
4703 g_return_val_if_reached (0);
4706 static GstColorBalanceType
4707 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4709 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4710 GstColorBalance *balance_element = NULL;
4711 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4713 GST_OBJECT_LOCK (playsink);
4714 if (playsink->colorbalance_element)
4716 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4717 GST_OBJECT_UNLOCK (playsink);
4719 if (balance_element) {
4720 t = gst_color_balance_get_balance_type (balance_element);
4721 gst_object_unref (balance_element);
4728 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4730 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4732 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4733 iface->set_value = gst_play_sink_colorbalance_set_value;
4734 iface->get_value = gst_play_sink_colorbalance_get_value;
4735 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4739 gst_play_sink_plugin_init (GstPlugin * plugin)
4741 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4742 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4743 GST_TYPE_PLAY_SINK);