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/interfaces/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))
51 /* holds the common data fields for the audio and video pipelines. We keep them
52 * in a structure to more easily have all the info available. */
55 GstPlaySink *playsink;
68 GstElement *volume; /* element with the volume property */
69 gboolean sink_volume; /* if the volume was provided by the sink */
70 GstElement *mute; /* element with the mute property */
72 GstElement *ts_offset;
78 GstPad *sinkpad, *srcpad;
80 GstElement *deinterlace;
81 } GstPlayVideoDeinterlaceChain;
91 GstElement *ts_offset;
100 GstElement *resample;
101 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
102 GstPad *vissinkpad; /* visualisation sinkpad, */
104 GstPad *vissrcpad; /* visualisation srcpad, */
105 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
114 GstElement *identity;
116 GstPad *videosinkpad;
118 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
120 GstElement *sink; /* custom sink to receive subtitle buffers */
123 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
124 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
125 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
126 g_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
127 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
129 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
130 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
131 g_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
134 #define PENDING_FLAG_SET(playsink, flagtype) \
135 ((playsink->pending_blocked_pads) |= (1 << flagtype))
136 #define PENDING_FLAG_UNSET(playsink, flagtype) \
137 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
138 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
139 ((playsink->pending_blocked_pads) & (1 << flagtype))
140 #define PENDING_VIDEO_BLOCK(playsink) \
141 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
142 #define PENDING_AUDIO_BLOCK(playsink) \
143 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
144 #define PENDING_TEXT_BLOCK(playsink) \
145 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
153 gboolean async_pending;
154 gboolean need_async_start;
158 GstStreamSynchronizer *stream_synchronizer;
161 GstPlayAudioChain *audiochain;
162 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
163 GstPlayVideoChain *videochain;
164 GstPlayVisChain *vischain;
165 GstPlayTextChain *textchain;
169 gboolean audio_pad_raw;
170 gboolean audio_pad_blocked;
171 GstPad *audio_srcpad_stream_synchronizer;
172 GstPad *audio_sinkpad_stream_synchronizer;
173 gulong audio_block_id;
175 GstElement *audio_tee;
176 GstPad *audio_tee_sink;
177 GstPad *audio_tee_asrc;
178 GstPad *audio_tee_vissrc;
181 gboolean video_pad_raw;
182 gboolean video_pad_blocked;
183 GstPad *video_srcpad_stream_synchronizer;
184 GstPad *video_sinkpad_stream_synchronizer;
185 gulong video_block_id;
188 gboolean text_pad_blocked;
189 GstPad *text_srcpad_stream_synchronizer;
190 GstPad *text_sinkpad_stream_synchronizer;
191 gulong text_block_id;
193 guint32 pending_blocked_pads;
196 GstElement *audio_sink;
197 GstElement *video_sink;
198 GstElement *visualisation;
199 GstElement *text_sink;
202 gchar *font_desc; /* font description */
203 gchar *subtitle_encoding; /* subtitle encoding */
204 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
206 gboolean volume_changed; /* volume/mute changed while no audiochain */
207 gboolean mute_changed; /* ... has been created yet */
210 /* videooverlay proxy interface */
211 GstVideoOverlay *overlay_element; /* protected with LOCK */
212 gboolean overlay_handle_set;
213 guintptr overlay_handle;
214 gboolean overlay_render_rectangle_set;
215 gint overlay_x, overlay_y, overlay_width, overlay_height;
216 gboolean overlay_handle_events_set;
217 gboolean overlay_handle_events;
219 /* colorbalance proxy interface */
220 GstColorBalance *colorbalance_element;
221 GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
222 gint colorbalance_values[4];
225 struct _GstPlaySinkClass
227 GstBinClass parent_class;
229 gboolean (*reconfigure) (GstPlaySink * playsink);
231 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
235 static GstStaticPadTemplate audiotemplate =
236 GST_STATIC_PAD_TEMPLATE ("audio_sink",
239 GST_STATIC_CAPS_ANY);
240 static GstStaticPadTemplate videotemplate =
241 GST_STATIC_PAD_TEMPLATE ("video_sink",
244 GST_STATIC_CAPS_ANY);
245 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
248 GST_STATIC_CAPS_ANY);
250 /* FIXME 0.11: Remove */
251 static GstStaticPadTemplate audiorawtemplate =
252 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
255 GST_STATIC_CAPS_ANY);
256 static GstStaticPadTemplate videorawtemplate =
257 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
260 GST_STATIC_CAPS_ANY);
271 PROP_SUBTITLE_ENCODING,
287 static void gst_play_sink_dispose (GObject * object);
288 static void gst_play_sink_finalize (GObject * object);
289 static void gst_play_sink_set_property (GObject * object, guint prop_id,
290 const GValue * value, GParamSpec * spec);
291 static void gst_play_sink_get_property (GObject * object, guint prop_id,
292 GValue * value, GParamSpec * spec);
294 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
295 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
296 static void gst_play_sink_release_request_pad (GstElement * element,
298 static gboolean gst_play_sink_send_event (GstElement * element,
300 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
301 GstStateChange transition);
303 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
305 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
306 GstPlaySink * playsink);
307 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
308 GstPlaySink * playsink);
310 static void update_av_offset (GstPlaySink * playsink);
312 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
314 static void gst_play_sink_overlay_init (gpointer g_iface,
315 gpointer g_iface_data);
316 static void gst_play_sink_navigation_init (gpointer g_iface,
317 gpointer g_iface_data);
318 static void gst_play_sink_colorbalance_init (gpointer g_iface,
319 gpointer g_iface_data);
322 _do_init (GType type)
324 static const GInterfaceInfo svol_info = {
327 static const GInterfaceInfo ov_info = {
328 gst_play_sink_overlay_init,
331 static const GInterfaceInfo nav_info = {
332 gst_play_sink_navigation_init,
335 static const GInterfaceInfo col_info = {
336 gst_play_sink_colorbalance_init,
340 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
341 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
342 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
343 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
346 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
347 _do_init (g_define_type_id));
350 gst_play_sink_class_init (GstPlaySinkClass * klass)
352 GObjectClass *gobject_klass;
353 GstElementClass *gstelement_klass;
354 GstBinClass *gstbin_klass;
356 gobject_klass = (GObjectClass *) klass;
357 gstelement_klass = (GstElementClass *) klass;
358 gstbin_klass = (GstBinClass *) klass;
360 gobject_klass->dispose = gst_play_sink_dispose;
361 gobject_klass->finalize = gst_play_sink_finalize;
362 gobject_klass->set_property = gst_play_sink_set_property;
363 gobject_klass->get_property = gst_play_sink_get_property;
369 * Control the behaviour of playsink.
371 g_object_class_install_property (gobject_klass, PROP_FLAGS,
372 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
373 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
374 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
377 * GstPlaySink:volume:
379 * Get or set the current audio stream volume. 1.0 means 100%,
380 * 0.0 means mute. This uses a linear volume scale.
383 g_object_class_install_property (gobject_klass, PROP_VOLUME,
384 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
385 0.0, VOLUME_MAX_DOUBLE, 1.0,
386 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
387 g_object_class_install_property (gobject_klass, PROP_MUTE,
388 g_param_spec_boolean ("mute", "Mute",
389 "Mute the audio channel without changing the volume", FALSE,
390 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
391 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
392 g_param_spec_string ("subtitle-font-desc",
393 "Subtitle font description",
394 "Pango font description of font "
395 "to be used for subtitle rendering", NULL,
396 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
397 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
398 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
399 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
400 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
401 "be checked for an encoding to use. If that is not set either, "
402 "ISO-8859-15 will be assumed.", NULL,
403 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
404 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
405 g_param_spec_object ("vis-plugin", "Vis plugin",
406 "the visualization element to use (NULL = default)",
407 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
409 * GstPlaySink:sample:
411 * Get the currently rendered or prerolled sample in the video sink.
412 * The #GstCaps in the sample will describe the format of the buffer.
414 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
415 g_param_spec_boxed ("sample", "Sample",
416 "The last sample (NULL = no video available)",
417 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
419 * GstPlaySink:av-offset:
421 * Control the synchronisation offset between the audio and video streams.
422 * Positive values make the audio ahead of the video and negative values make
423 * the audio go behind the video.
427 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
428 g_param_spec_int64 ("av-offset", "AV Offset",
429 "The synchronisation offset between audio and video in nanoseconds",
430 G_MININT64, G_MAXINT64, 0,
431 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
434 * GstPlaySink:video-sink:
436 * Set the used video sink element. NULL will use the default sink. playsink
437 * must be in %GST_STATE_NULL
441 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
442 g_param_spec_object ("video-sink", "Video Sink",
443 "the video output element to use (NULL = default sink)",
444 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
446 * GstPlaySink:audio-sink:
448 * Set the used audio sink element. NULL will use the default sink. playsink
449 * must be in %GST_STATE_NULL
453 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
454 g_param_spec_object ("audio-sink", "Audio Sink",
455 "the audio output element to use (NULL = default sink)",
456 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
459 * GstPlaySink:text-sink:
461 * Set the used text sink element. NULL will use the default sink. playsink
462 * must be in %GST_STATE_NULL
466 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
467 g_param_spec_object ("text-sink", "Text sink",
468 "the text output element to use (NULL = default textoverlay)",
469 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
472 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
473 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
474 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
477 * GstPlaySink::convert-sample
478 * @playsink: a #GstPlaySink
479 * @caps: the target format of the sample
481 * Action signal to retrieve the currently playing video sample in the format
482 * specified by @caps.
483 * If @caps is %NULL, no conversion will be performed and this function is
484 * equivalent to the #GstPlaySink::sample property.
486 * Returns: a #GstSample of the current video sample converted to #caps.
487 * The caps in the sample will describe the final layout of the buffer data.
488 * %NULL is returned when no current sample can be retrieved or when the
491 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
492 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
493 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
494 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
496 gst_element_class_add_pad_template (gstelement_klass,
497 gst_static_pad_template_get (&audiorawtemplate));
498 gst_element_class_add_pad_template (gstelement_klass,
499 gst_static_pad_template_get (&audiotemplate));
500 gst_element_class_add_pad_template (gstelement_klass,
501 gst_static_pad_template_get (&videorawtemplate));
502 gst_element_class_add_pad_template (gstelement_klass,
503 gst_static_pad_template_get (&videotemplate));
504 gst_element_class_add_pad_template (gstelement_klass,
505 gst_static_pad_template_get (&texttemplate));
506 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
508 "Convenience sink for multiple streams",
509 "Wim Taymans <wim.taymans@gmail.com>");
511 gstelement_klass->change_state =
512 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
513 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
514 gstelement_klass->request_new_pad =
515 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
516 gstelement_klass->release_pad =
517 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
519 gstbin_klass->handle_message =
520 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
522 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
523 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
527 gst_play_sink_init (GstPlaySink * playsink)
529 GstColorBalanceChannel *channel;
532 playsink->video_sink = NULL;
533 playsink->audio_sink = NULL;
534 playsink->visualisation = NULL;
535 playsink->text_sink = NULL;
536 playsink->volume = 1.0;
537 playsink->font_desc = NULL;
538 playsink->subtitle_encoding = NULL;
539 playsink->flags = DEFAULT_FLAGS;
541 playsink->stream_synchronizer =
542 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
543 gst_bin_add (GST_BIN_CAST (playsink),
544 GST_ELEMENT_CAST (playsink->stream_synchronizer));
546 g_rec_mutex_init (&playsink->lock);
547 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
550 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
552 channel->label = g_strdup ("CONTRAST");
553 channel->min_value = -1000;
554 channel->max_value = 1000;
555 playsink->colorbalance_channels =
556 g_list_append (playsink->colorbalance_channels, channel);
557 playsink->colorbalance_values[0] = 0;
560 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
562 channel->label = g_strdup ("BRIGHTNESS");
563 channel->min_value = -1000;
564 channel->max_value = 1000;
565 playsink->colorbalance_channels =
566 g_list_append (playsink->colorbalance_channels, channel);
567 playsink->colorbalance_values[1] = 0;
570 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
572 channel->label = g_strdup ("HUE");
573 channel->min_value = -1000;
574 channel->max_value = 1000;
575 playsink->colorbalance_channels =
576 g_list_append (playsink->colorbalance_channels, channel);
577 playsink->colorbalance_values[2] = 0;
580 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
582 channel->label = g_strdup ("SATURATION");
583 channel->min_value = -1000;
584 channel->max_value = 1000;
585 playsink->colorbalance_channels =
586 g_list_append (playsink->colorbalance_channels, channel);
587 playsink->colorbalance_values[3] = 0;
591 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
595 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
598 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
604 free_chain (GstPlayChain * chain)
608 gst_object_unref (chain->bin);
614 gst_play_sink_dispose (GObject * object)
616 GstPlaySink *playsink;
618 playsink = GST_PLAY_SINK (object);
620 if (playsink->audio_sink != NULL) {
621 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
622 gst_object_unref (playsink->audio_sink);
623 playsink->audio_sink = NULL;
625 if (playsink->video_sink != NULL) {
626 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
627 gst_object_unref (playsink->video_sink);
628 playsink->video_sink = NULL;
630 if (playsink->visualisation != NULL) {
631 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
632 gst_object_unref (playsink->visualisation);
633 playsink->visualisation = NULL;
635 if (playsink->text_sink != NULL) {
636 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
637 gst_object_unref (playsink->text_sink);
638 playsink->text_sink = NULL;
641 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
642 playsink->videodeinterlacechain = NULL;
643 free_chain ((GstPlayChain *) playsink->videochain);
644 playsink->videochain = NULL;
645 free_chain ((GstPlayChain *) playsink->audiochain);
646 playsink->audiochain = NULL;
647 free_chain ((GstPlayChain *) playsink->vischain);
648 playsink->vischain = NULL;
649 free_chain ((GstPlayChain *) playsink->textchain);
650 playsink->textchain = NULL;
652 if (playsink->audio_tee_sink) {
653 gst_object_unref (playsink->audio_tee_sink);
654 playsink->audio_tee_sink = NULL;
657 if (playsink->audio_tee_vissrc) {
658 gst_element_release_request_pad (playsink->audio_tee,
659 playsink->audio_tee_vissrc);
660 gst_object_unref (playsink->audio_tee_vissrc);
661 playsink->audio_tee_vissrc = NULL;
664 if (playsink->audio_tee_asrc) {
665 gst_element_release_request_pad (playsink->audio_tee,
666 playsink->audio_tee_asrc);
667 gst_object_unref (playsink->audio_tee_asrc);
668 playsink->audio_tee_asrc = NULL;
671 g_free (playsink->font_desc);
672 playsink->font_desc = NULL;
674 g_free (playsink->subtitle_encoding);
675 playsink->subtitle_encoding = NULL;
677 playsink->stream_synchronizer = NULL;
679 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
681 g_list_free (playsink->colorbalance_channels);
682 playsink->colorbalance_channels = NULL;
684 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
688 gst_play_sink_finalize (GObject * object)
690 GstPlaySink *playsink;
692 playsink = GST_PLAY_SINK (object);
694 g_rec_mutex_clear (&playsink->lock);
696 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
700 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
703 GstElement **elem = NULL, *old = NULL;
705 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
707 GST_PLAY_SINK_LOCK (playsink);
709 case GST_PLAY_SINK_TYPE_AUDIO:
710 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
711 elem = &playsink->audio_sink;
713 case GST_PLAY_SINK_TYPE_VIDEO:
714 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
715 elem = &playsink->video_sink;
717 case GST_PLAY_SINK_TYPE_TEXT:
718 elem = &playsink->text_sink;
726 gst_object_ref (sink);
729 GST_PLAY_SINK_UNLOCK (playsink);
733 gst_element_set_state (old, GST_STATE_NULL);
734 gst_object_unref (old);
739 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
741 GstElement *result = NULL;
742 GstElement *elem = NULL, *chainp = NULL;
744 GST_PLAY_SINK_LOCK (playsink);
746 case GST_PLAY_SINK_TYPE_AUDIO:
747 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
749 GstPlayAudioChain *chain;
750 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
751 chainp = chain->sink;
752 elem = playsink->audio_sink;
755 case GST_PLAY_SINK_TYPE_VIDEO:
756 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
758 GstPlayVideoChain *chain;
759 if ((chain = (GstPlayVideoChain *) playsink->videochain))
760 chainp = chain->sink;
761 elem = playsink->video_sink;
764 case GST_PLAY_SINK_TYPE_TEXT:
766 GstPlayTextChain *chain;
767 if ((chain = (GstPlayTextChain *) playsink->textchain))
768 chainp = chain->sink;
769 elem = playsink->text_sink;
776 /* we have an active chain with a sink, get the sink */
777 result = gst_object_ref (chainp);
779 /* nothing found, return last configured sink */
780 if (result == NULL && elem)
781 result = gst_object_ref (elem);
782 GST_PLAY_SINK_UNLOCK (playsink);
787 static GstPadProbeReturn
788 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
791 GstPlaySink *playsink;
792 GstPlayVisChain *chain;
794 playsink = GST_PLAY_SINK (user_data);
796 GST_PLAY_SINK_LOCK (playsink);
797 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
798 /* now try to change the plugin in the running vis chain */
799 if (!(chain = (GstPlayVisChain *) playsink->vischain))
802 /* unlink the old plugin and unghost the pad */
803 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
804 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
806 /* set the old plugin to NULL and remove */
807 gst_element_set_state (chain->vis, GST_STATE_NULL);
808 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
810 /* add new plugin and set state to playing */
811 chain->vis = playsink->visualisation;
812 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
813 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
816 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
817 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
820 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
821 GST_PAD_LINK_CHECK_NOTHING);
822 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
826 GST_PLAY_SINK_UNLOCK (playsink);
828 /* remove the probe and unblock the pad */
829 return GST_PAD_PROBE_REMOVE;
833 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
835 GstPlayVisChain *chain;
837 /* setting NULL means creating the default vis plugin */
839 vis = gst_element_factory_make ("goom", "vis");
841 /* simply return if we don't have a vis plugin here */
845 GST_PLAY_SINK_LOCK (playsink);
846 /* first store the new vis */
847 if (playsink->visualisation)
848 gst_object_unref (playsink->visualisation);
850 gst_object_ref_sink (vis);
851 playsink->visualisation = vis;
853 /* now try to change the plugin in the running vis chain, if we have no chain,
854 * we don't bother, any future vis chain will be created with the new vis
856 if (!(chain = (GstPlayVisChain *) playsink->vischain))
859 /* block the pad, the next time the callback is called we can change the
860 * visualisation. It's possible that this never happens or that the pad was
861 * already blocked. If the callback never happens, we don't have new data so
862 * we don't need the new vis plugin. If the pad was already blocked, the
863 * function returns FALSE but the previous pad block will do the right thing
865 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
866 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
867 gst_play_sink_vis_blocked, playsink, NULL);
869 GST_PLAY_SINK_UNLOCK (playsink);
875 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
877 GstElement *result = NULL;
878 GstPlayVisChain *chain;
880 GST_PLAY_SINK_LOCK (playsink);
881 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
882 /* we have an active chain, get the sink */
884 result = gst_object_ref (chain->vis);
886 /* nothing found, return last configured sink */
887 if (result == NULL && playsink->visualisation)
888 result = gst_object_ref (playsink->visualisation);
889 GST_PLAY_SINK_UNLOCK (playsink);
895 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
897 GstPlayAudioChain *chain;
899 GST_PLAY_SINK_LOCK (playsink);
900 playsink->volume = volume;
901 chain = (GstPlayAudioChain *) playsink->audiochain;
902 if (chain && chain->volume) {
903 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
904 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
905 chain->mute, volume, playsink->mute);
906 /* if there is a mute element or we are not muted, set the volume */
907 if (chain->mute || !playsink->mute)
908 g_object_set (chain->volume, "volume", volume, NULL);
910 GST_LOG_OBJECT (playsink, "no volume element");
911 playsink->volume_changed = TRUE;
913 GST_PLAY_SINK_UNLOCK (playsink);
917 gst_play_sink_get_volume (GstPlaySink * playsink)
920 GstPlayAudioChain *chain;
922 GST_PLAY_SINK_LOCK (playsink);
923 chain = (GstPlayAudioChain *) playsink->audiochain;
924 result = playsink->volume;
925 if (chain && chain->volume) {
926 if (chain->mute || !playsink->mute) {
927 g_object_get (chain->volume, "volume", &result, NULL);
928 playsink->volume = result;
931 GST_PLAY_SINK_UNLOCK (playsink);
937 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
939 GstPlayAudioChain *chain;
941 GST_PLAY_SINK_LOCK (playsink);
942 playsink->mute = mute;
943 chain = (GstPlayAudioChain *) playsink->audiochain;
946 g_object_set (chain->mute, "mute", mute, NULL);
947 } else if (chain->volume) {
949 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
951 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
955 playsink->mute_changed = TRUE;
957 GST_PLAY_SINK_UNLOCK (playsink);
961 gst_play_sink_get_mute (GstPlaySink * playsink)
964 GstPlayAudioChain *chain;
966 GST_PLAY_SINK_LOCK (playsink);
967 chain = (GstPlayAudioChain *) playsink->audiochain;
968 if (chain && chain->mute) {
969 g_object_get (chain->mute, "mute", &result, NULL);
970 playsink->mute = result;
972 result = playsink->mute;
974 GST_PLAY_SINK_UNLOCK (playsink);
980 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
984 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
985 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
989 add_chain (GstPlayChain * chain, gboolean add)
991 if (chain->added == add)
995 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
997 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
998 /* we don't want to lose our sink status */
999 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1008 activate_chain (GstPlayChain * chain, gboolean activate)
1012 if (chain->activated == activate)
1015 GST_OBJECT_LOCK (chain->playsink);
1016 state = GST_STATE_TARGET (chain->playsink);
1017 GST_OBJECT_UNLOCK (chain->playsink);
1020 gst_element_set_state (chain->bin, state);
1022 gst_element_set_state (chain->bin, GST_STATE_NULL);
1024 chain->activated = activate;
1030 element_is_sink (GstElement * element)
1034 GST_OBJECT_LOCK (element);
1035 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1036 GST_OBJECT_UNLOCK (element);
1038 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1043 element_has_property (GstElement * element, const gchar * pname, GType type)
1047 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1049 if (pspec == NULL) {
1050 GST_DEBUG_OBJECT (element, "no %s property", pname);
1054 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1055 g_type_is_a (pspec->value_type, type)) {
1056 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1057 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1061 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1062 "and we expected it to be of type %s", pname,
1063 g_type_name (pspec->value_type), g_type_name (type));
1070 const gchar *prop_name;
1073 } FindPropertyHelper;
1076 find_property (const GValue * item, FindPropertyHelper * helper)
1078 GstElement *element = g_value_get_object (item);
1079 if (helper->need_sink && !element_is_sink (element)) {
1083 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1087 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1088 (helper->need_sink) ? "sink" : "element");
1089 return 0; /* keep it */
1092 /* FIXME: why not move these functions into core? */
1093 /* find a sink in the hierarchy with a property named @name. This function does
1094 * not increase the refcount of the returned object and thus remains valid as
1095 * long as the bin is valid. */
1097 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1098 const gchar * name, GType expected_type)
1100 GstElement *result = NULL;
1103 if (element_has_property (obj, name, expected_type)) {
1105 } else if (GST_IS_BIN (obj)) {
1107 GValue item = { 0, };
1108 FindPropertyHelper helper = { name, expected_type, TRUE };
1110 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1111 found = gst_iterator_find_custom (it,
1112 (GCompareFunc) find_property, &item, &helper);
1113 gst_iterator_free (it);
1115 result = g_value_get_object (&item);
1116 /* we don't need the extra ref */
1117 g_value_unset (&item);
1123 /* find an object in the hierarchy with a property named @name */
1125 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1126 const gchar * name, GType expected_type)
1128 GstElement *result = NULL;
1131 if (GST_IS_BIN (obj)) {
1133 GValue item = { 0, };
1134 FindPropertyHelper helper = { name, expected_type, FALSE };
1136 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1137 found = gst_iterator_find_custom (it,
1138 (GCompareFunc) find_property, &item, &helper);
1139 gst_iterator_free (it);
1141 result = g_value_dup_object (&item);
1142 g_value_unset (&item);
1144 if (element_has_property (obj, name, expected_type)) {
1146 gst_object_ref (obj);
1153 do_async_start (GstPlaySink * playsink)
1155 GstMessage *message;
1157 if (!playsink->need_async_start) {
1158 GST_INFO_OBJECT (playsink, "no async_start needed");
1162 playsink->async_pending = TRUE;
1164 GST_INFO_OBJECT (playsink, "Sending async_start message");
1165 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1166 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1167 (playsink), message);
1171 do_async_done (GstPlaySink * playsink)
1173 GstMessage *message;
1175 if (playsink->async_pending) {
1176 GST_INFO_OBJECT (playsink, "Sending async_done message");
1177 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1178 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1179 (playsink), message);
1181 playsink->async_pending = FALSE;
1184 playsink->need_async_start = FALSE;
1187 /* try to change the state of an element. This function returns the element when
1188 * the state change could be performed. When this function returns NULL an error
1189 * occured and the element is unreffed if @unref is TRUE. */
1191 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1193 GstStateChangeReturn ret;
1196 ret = gst_element_set_state (element, GST_STATE_READY);
1197 if (ret == GST_STATE_CHANGE_FAILURE) {
1198 GST_DEBUG_OBJECT (playsink, "failed state change..");
1199 gst_element_set_state (element, GST_STATE_NULL);
1201 gst_object_unref (element);
1208 /* make the element (bin) that contains the elements needed to perform
1209 * video display. Only used for *raw* video streams.
1211 * +------------------------------------------------------------+
1213 * | +-------+ +----------+ +----------+ +---------+ |
1214 * | | queue | |colorspace| |videoscale| |videosink| |
1215 * | +-sink src-sink src-sink src-sink | |
1216 * | | +-------+ +----------+ +----------+ +---------+ |
1218 * +------------------------------------------------------------+
1221 static GstPlayVideoDeinterlaceChain *
1222 gen_video_deinterlace_chain (GstPlaySink * playsink)
1224 GstPlayVideoDeinterlaceChain *chain;
1227 GstElement *head = NULL, *prev = NULL;
1229 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1230 chain->chain.playsink = playsink;
1232 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1234 /* create a bin to hold objects, as we create them we add them to this bin so
1235 * that when something goes wrong we only need to unref the bin */
1236 chain->chain.bin = gst_bin_new ("vdbin");
1237 bin = GST_BIN_CAST (chain->chain.bin);
1238 gst_object_ref_sink (bin);
1240 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1241 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1242 if (chain->conv == NULL) {
1243 post_missing_element_message (playsink, COLORSPACE);
1244 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1245 (_("Missing element '%s' - check your GStreamer installation."),
1246 COLORSPACE), ("video rendering might fail"));
1248 gst_bin_add (bin, chain->conv);
1253 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1254 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1255 if (chain->deinterlace == NULL) {
1256 post_missing_element_message (playsink, "deinterlace");
1257 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1258 (_("Missing element '%s' - check your GStreamer installation."),
1259 "deinterlace"), ("deinterlacing won't work"));
1261 gst_bin_add (bin, chain->deinterlace);
1263 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1264 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1267 head = chain->deinterlace;
1269 prev = chain->deinterlace;
1273 pad = gst_element_get_static_pad (head, "sink");
1274 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1275 gst_object_unref (pad);
1277 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1281 pad = gst_element_get_static_pad (prev, "src");
1282 chain->srcpad = gst_ghost_pad_new ("src", pad);
1283 gst_object_unref (pad);
1285 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1288 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1289 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1295 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1296 (NULL), ("Failed to configure the video deinterlace chain."));
1297 free_chain ((GstPlayChain *) chain);
1303 is_valid_color_balance_element (GstColorBalance * bal)
1305 gboolean have_brightness = FALSE;
1306 gboolean have_contrast = FALSE;
1307 gboolean have_hue = FALSE;
1308 gboolean have_saturation = FALSE;
1309 const GList *channels, *l;
1311 channels = gst_color_balance_list_channels (bal);
1312 for (l = channels; l; l = l->next) {
1313 GstColorBalanceChannel *ch = l->data;
1315 if (g_strrstr (ch->label, "BRIGHTNESS"))
1316 have_brightness = TRUE;
1317 else if (g_strrstr (ch->label, "CONTRAST"))
1318 have_contrast = TRUE;
1319 else if (g_strrstr (ch->label, "HUE"))
1321 else if (g_strrstr (ch->label, "SATURATION"))
1322 have_saturation = TRUE;
1325 return have_brightness && have_contrast && have_hue && have_saturation;
1329 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1332 GstColorBalance *cb, **cb_out = user_data;
1334 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1335 valid = is_valid_color_balance_element (cb);
1338 && gst_color_balance_get_balance_type (*cb_out) ==
1339 GST_COLOR_BALANCE_SOFTWARE) {
1340 gst_object_unref (*cb_out);
1341 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1342 } else if (!*cb_out) {
1343 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1348 static GstColorBalance *
1349 find_color_balance_element (GstElement * element)
1352 GstColorBalance *cb = NULL;
1354 if (GST_IS_COLOR_BALANCE (element)
1355 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1356 return GST_COLOR_BALANCE (gst_object_ref (element));
1357 else if (!GST_IS_BIN (element))
1360 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1361 GST_TYPE_COLOR_BALANCE);
1362 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1363 &cb) == GST_ITERATOR_RESYNC)
1364 gst_iterator_resync (it);
1365 gst_iterator_free (it);
1371 colorbalance_value_changed_cb (GstColorBalance * balance,
1372 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1377 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1378 GstColorBalanceChannel *proxy = l->data;
1380 if (g_strrstr (channel->label, proxy->label)) {
1383 /* Convert to [0, 1] range */
1386 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1387 (gdouble) channel->min_value);
1388 /* Convert to proxy range */
1390 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1391 (gdouble) proxy->min_value);
1392 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1394 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1395 playsink->colorbalance_values[i]);
1402 update_colorbalance (GstPlaySink * playsink)
1404 GstColorBalance *balance = NULL;
1408 GST_OBJECT_LOCK (playsink);
1409 if (playsink->colorbalance_element) {
1411 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1413 GST_OBJECT_UNLOCK (playsink);
1417 g_signal_handlers_block_by_func (balance,
1418 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1420 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1421 GstColorBalanceChannel *proxy = l->data;
1422 GstColorBalanceChannel *channel = NULL;
1423 const GList *channels, *k;
1425 channels = gst_color_balance_list_channels (balance);
1426 for (k = channels; k; k = k->next) {
1427 GstColorBalanceChannel *tmp = k->data;
1429 if (g_strrstr (tmp->label, proxy->label)) {
1437 gst_color_balance_set_value (balance, channel,
1438 playsink->colorbalance_values[i]);
1441 g_signal_handlers_unblock_by_func (balance,
1442 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1445 /* make the element (bin) that contains the elements needed to perform
1448 * +------------------------------------------------------------+
1450 * | +-------+ +----------+ +----------+ +---------+ |
1451 * | | queue | |colorspace| |videoscale| |videosink| |
1452 * | +-sink src-sink src-sink src-sink | |
1453 * | | +-------+ +----------+ +----------+ +---------+ |
1455 * +------------------------------------------------------------+
1458 static GstPlayVideoChain *
1459 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1461 GstPlayVideoChain *chain;
1464 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1466 chain = g_new0 (GstPlayVideoChain, 1);
1467 chain->chain.playsink = playsink;
1468 chain->chain.raw = raw;
1470 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1472 if (playsink->video_sink) {
1473 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1474 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1476 /* only try fallback if no specific sink was chosen */
1477 if (chain->sink == NULL) {
1478 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1479 elem = gst_element_factory_make ("autovideosink", "videosink");
1480 chain->sink = try_element (playsink, elem, TRUE);
1482 if (chain->sink == NULL) {
1483 /* if default sink from config.h is different then try it too */
1484 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1485 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1486 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1487 chain->sink = try_element (playsink, elem, TRUE);
1491 playsink->video_sink = gst_object_ref (chain->sink);
1493 if (chain->sink == NULL)
1497 /* if we can disable async behaviour of the sink, we can avoid adding a
1498 * queue for the audio chain. */
1500 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1503 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1504 async, GST_ELEMENT_NAME (elem));
1505 g_object_set (elem, "async", async, NULL);
1506 chain->async = async;
1508 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1509 chain->async = TRUE;
1512 /* Make sure the aspect ratio is kept */
1514 gst_play_sink_find_property_sinks (playsink, chain->sink,
1515 "force-aspect-ratio", G_TYPE_BOOLEAN);
1517 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1519 /* find ts-offset element */
1520 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1521 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1524 /* create a bin to hold objects, as we create them we add them to this bin so
1525 * that when something goes wrong we only need to unref the bin */
1526 chain->chain.bin = gst_bin_new ("vbin");
1527 bin = GST_BIN_CAST (chain->chain.bin);
1528 gst_object_ref_sink (bin);
1529 gst_bin_add (bin, chain->sink);
1531 /* Get the VideoOverlay element */
1533 GstVideoOverlay *overlay = NULL;
1535 GST_OBJECT_LOCK (playsink);
1536 if (playsink->overlay_element)
1537 gst_object_unref (playsink->overlay_element);
1538 playsink->overlay_element =
1539 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1540 GST_TYPE_VIDEO_OVERLAY));
1541 if (playsink->overlay_element)
1542 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1543 GST_OBJECT_UNLOCK (playsink);
1546 if (playsink->overlay_handle_set)
1547 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1548 if (playsink->overlay_handle_events_set)
1549 gst_video_overlay_handle_events (overlay,
1550 playsink->overlay_handle_events);
1551 if (playsink->overlay_render_rectangle_set)
1552 gst_video_overlay_set_render_rectangle (overlay,
1553 playsink->overlay_x, playsink->overlay_y,
1554 playsink->overlay_width, playsink->overlay_height);
1555 gst_object_unref (overlay);
1559 /* decouple decoder from sink, this improves playback quite a lot since the
1560 * decoder can continue while the sink blocks for synchronisation. We don't
1561 * need a lot of buffers as this consumes a lot of memory and we don't want
1562 * too little because else we would be context switching too quickly. */
1563 chain->queue = gst_element_factory_make ("queue", "vqueue");
1564 if (chain->queue == NULL) {
1565 post_missing_element_message (playsink, "queue");
1566 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1567 (_("Missing element '%s' - check your GStreamer installation."),
1568 "queue"), ("video rendering might be suboptimal"));
1572 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1573 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1574 gst_bin_add (bin, chain->queue);
1575 head = prev = chain->queue;
1578 GST_OBJECT_LOCK (playsink);
1579 if (playsink->colorbalance_element) {
1580 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1581 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1582 gst_object_unref (playsink->colorbalance_element);
1584 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1585 GST_OBJECT_UNLOCK (playsink);
1587 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1588 || (!playsink->colorbalance_element
1589 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1590 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1591 gboolean use_balance = !playsink->colorbalance_element
1592 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1594 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1596 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1597 "use-converters", use_converters, "use-balance", use_balance, NULL);
1599 GST_OBJECT_LOCK (playsink);
1600 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1601 playsink->colorbalance_element =
1602 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1603 (chain->conv)->balance));
1604 GST_OBJECT_UNLOCK (playsink);
1606 gst_bin_add (bin, chain->conv);
1608 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1609 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1617 update_colorbalance (playsink);
1620 GST_DEBUG_OBJECT (playsink, "linking to sink");
1621 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1622 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1626 pad = gst_element_get_static_pad (head, "sink");
1627 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1628 gst_object_unref (pad);
1630 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1637 if (!elem && !playsink->video_sink) {
1638 post_missing_element_message (playsink, "autovideosink");
1639 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1640 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1641 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1642 (_("Both autovideosink and %s elements are missing."),
1643 DEFAULT_VIDEOSINK), (NULL));
1645 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1646 (_("The autovideosink element is missing.")), (NULL));
1649 if (playsink->video_sink) {
1650 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1651 (_("Configured videosink %s is not working."),
1652 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1653 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1654 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1655 (_("Both autovideosink and %s elements are not working."),
1656 DEFAULT_VIDEOSINK), (NULL));
1658 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1659 (_("The autovideosink element is not working.")), (NULL));
1662 free_chain ((GstPlayChain *) chain);
1668 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1669 (NULL), ("Failed to configure the video sink."));
1670 /* checking sink made it READY */
1671 gst_element_set_state (chain->sink, GST_STATE_NULL);
1672 /* Remove chain from the bin to allow reuse later */
1673 gst_bin_remove (bin, chain->sink);
1674 free_chain ((GstPlayChain *) chain);
1680 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1683 GstPlayVideoChain *chain;
1684 GstStateChangeReturn ret;
1686 chain = playsink->videochain;
1688 chain->chain.raw = raw;
1690 /* if the chain was active we don't do anything */
1691 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1694 /* try to set the sink element to READY again */
1695 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1696 if (ret == GST_STATE_CHANGE_FAILURE)
1699 /* Get the VideoOverlay element */
1701 GstVideoOverlay *overlay = NULL;
1703 GST_OBJECT_LOCK (playsink);
1704 if (playsink->overlay_element)
1705 gst_object_unref (playsink->overlay_element);
1706 playsink->overlay_element =
1707 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1708 GST_TYPE_VIDEO_OVERLAY));
1709 if (playsink->overlay_element)
1710 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1711 GST_OBJECT_UNLOCK (playsink);
1714 if (playsink->overlay_handle_set)
1715 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1716 if (playsink->overlay_handle_events_set)
1717 gst_video_overlay_handle_events (overlay,
1718 playsink->overlay_handle_events);
1719 if (playsink->overlay_render_rectangle_set)
1720 gst_video_overlay_set_render_rectangle (overlay,
1721 playsink->overlay_x, playsink->overlay_y,
1722 playsink->overlay_width, playsink->overlay_height);
1723 gst_object_unref (overlay);
1727 /* find ts-offset element */
1728 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1729 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1732 /* if we can disable async behaviour of the sink, we can avoid adding a
1733 * queue for the audio chain. */
1735 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1738 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1739 async, GST_ELEMENT_NAME (elem));
1740 g_object_set (elem, "async", async, NULL);
1741 chain->async = async;
1743 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1744 chain->async = TRUE;
1747 /* Make sure the aspect ratio is kept */
1749 gst_play_sink_find_property_sinks (playsink, chain->sink,
1750 "force-aspect-ratio", G_TYPE_BOOLEAN);
1752 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1754 GST_OBJECT_LOCK (playsink);
1755 if (playsink->colorbalance_element) {
1756 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1757 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1758 gst_object_unref (playsink->colorbalance_element);
1760 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1761 GST_OBJECT_UNLOCK (playsink);
1764 gboolean use_balance = !playsink->colorbalance_element
1765 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1767 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1769 GST_OBJECT_LOCK (playsink);
1770 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1771 playsink->colorbalance_element =
1772 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1773 (chain->conv)->balance));
1774 GST_OBJECT_UNLOCK (playsink);
1777 update_colorbalance (playsink);
1782 /* make an element for playback of video with subtitles embedded.
1783 * Only used for *raw* video streams.
1785 * +--------------------------------------------+
1787 * | +--------+ +-----------------+ |
1788 * | | queue | | subtitleoverlay | |
1789 * video--src sink---video_sink | |
1790 * | +--------+ | src--src
1791 * text------------------text_sink | |
1792 * | +-----------------+ |
1793 * +--------------------------------------------+
1796 static GstPlayTextChain *
1797 gen_text_chain (GstPlaySink * playsink)
1799 GstPlayTextChain *chain;
1802 GstPad *videosinkpad, *textsinkpad, *srcpad;
1804 chain = g_new0 (GstPlayTextChain, 1);
1805 chain->chain.playsink = playsink;
1807 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1809 chain->chain.bin = gst_bin_new ("tbin");
1810 bin = GST_BIN_CAST (chain->chain.bin);
1811 gst_object_ref_sink (bin);
1813 videosinkpad = textsinkpad = srcpad = NULL;
1815 /* first try to hook the text pad to the custom sink */
1816 if (playsink->text_sink) {
1817 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1818 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1821 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1824 /* make sure the sparse subtitles don't participate in the preroll */
1825 g_object_set (elem, "async", FALSE, NULL);
1826 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1827 gst_bin_add (bin, chain->sink);
1828 /* NOTE streamsynchronizer needs streams decoupled */
1829 /* make a little queue */
1830 chain->queue = gst_element_factory_make ("queue", "subqueue");
1831 if (chain->queue == NULL) {
1832 post_missing_element_message (playsink, "queue");
1833 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1834 (_("Missing element '%s' - check your GStreamer installation."),
1835 "queue"), ("rendering might be suboptimal"));
1837 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1838 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1839 "silent", TRUE, NULL);
1840 gst_bin_add (bin, chain->queue);
1842 /* we have a custom sink, this will be our textsinkpad */
1843 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1844 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1845 /* we're all fine now and we can add the sink to the chain */
1846 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1847 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1849 GST_WARNING_OBJECT (playsink,
1850 "can't find a sink pad on custom text sink");
1851 gst_bin_remove (bin, chain->sink);
1852 gst_bin_remove (bin, chain->queue);
1854 chain->queue = NULL;
1856 /* try to set sync to true but it's no biggie when we can't */
1857 if (chain->sink && (elem =
1858 gst_play_sink_find_property_sinks (playsink, chain->sink,
1859 "sync", G_TYPE_BOOLEAN)))
1860 g_object_set (elem, "sync", TRUE, NULL);
1863 gst_bin_remove (bin, chain->sink);
1865 GST_WARNING_OBJECT (playsink,
1866 "can't find async property in custom text sink");
1869 if (textsinkpad == NULL) {
1870 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1871 (_("Custom text sink element is not usable.")),
1872 ("fallback to default textoverlay"));
1876 if (textsinkpad == NULL) {
1877 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1878 /* make a little queue */
1879 chain->queue = gst_element_factory_make ("queue", "vqueue");
1880 if (chain->queue == NULL) {
1881 post_missing_element_message (playsink, "queue");
1882 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1883 (_("Missing element '%s' - check your GStreamer installation."),
1884 "queue"), ("video rendering might be suboptimal"));
1886 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1887 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1888 "silent", TRUE, NULL);
1889 gst_bin_add (bin, chain->queue);
1890 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1894 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1895 if (chain->overlay == NULL) {
1896 post_missing_element_message (playsink, "subtitleoverlay");
1897 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1898 (_("Missing element '%s' - check your GStreamer installation."),
1899 "subtitleoverlay"), ("subtitle rendering disabled"));
1901 GstElement *element;
1903 gst_bin_add (bin, chain->overlay);
1905 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1906 if (playsink->font_desc) {
1907 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1908 playsink->font_desc, NULL);
1910 if (playsink->subtitle_encoding) {
1911 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1912 playsink->subtitle_encoding, NULL);
1915 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1916 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1918 /* make another little queue to decouple streams */
1919 element = gst_element_factory_make ("queue", "subqueue");
1920 if (element == NULL) {
1921 post_missing_element_message (playsink, "queue");
1922 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1923 (_("Missing element '%s' - check your GStreamer installation."),
1924 "queue"), ("rendering might be suboptimal"));
1926 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1927 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1928 "silent", TRUE, NULL);
1929 gst_bin_add (bin, element);
1930 if (gst_element_link_pads_full (element, "src", chain->overlay,
1931 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1932 textsinkpad = gst_element_get_static_pad (element, "sink");
1933 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1935 gst_bin_remove (bin, chain->sink);
1936 gst_bin_remove (bin, chain->overlay);
1938 chain->overlay = NULL;
1939 gst_object_unref (videosinkpad);
1940 videosinkpad = NULL;
1947 if (videosinkpad == NULL) {
1948 /* if we still don't have a videosink, we don't have an overlay. the only
1949 * thing we can do is insert an identity and ghost the src
1951 chain->identity = gst_element_factory_make ("identity", "tidentity");
1952 if (chain->identity == NULL) {
1953 post_missing_element_message (playsink, "identity");
1954 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1955 (_("Missing element '%s' - check your GStreamer installation."),
1956 "identity"), (NULL));
1958 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1959 g_object_set (chain->identity, "silent", TRUE, NULL);
1960 gst_bin_add (bin, chain->identity);
1961 srcpad = gst_element_get_static_pad (chain->identity, "src");
1962 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1966 /* expose the ghostpads */
1968 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1969 gst_object_unref (videosinkpad);
1970 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1973 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1974 gst_object_unref (textsinkpad);
1975 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1978 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1979 gst_object_unref (srcpad);
1980 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1987 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1991 g_object_get (object, "volume", &vol, NULL);
1992 playsink->volume = vol;
1994 g_object_notify (G_OBJECT (playsink), "volume");
1998 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2002 g_object_get (object, "mute", &mute, NULL);
2003 playsink->mute = mute;
2005 g_object_notify (G_OBJECT (playsink), "mute");
2008 /* make the chain that contains the elements needed to perform
2011 * We add a tee as the first element so that we can link the visualisation chain
2012 * to it when requested.
2014 * +-------------------------------------------------------------+
2016 * | +---------+ +----------+ +---------+ +---------+ |
2017 * | |audioconv| |audioscale| | volume | |audiosink| |
2018 * | +-srck src-sink src-sink src-sink | |
2019 * | | +---------+ +----------+ +---------+ +---------+ |
2021 * +-------------------------------------------------------------+
2023 static GstPlayAudioChain *
2024 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2026 GstPlayAudioChain *chain;
2028 gboolean have_volume;
2030 GstElement *head, *prev, *elem = NULL;
2032 chain = g_new0 (GstPlayAudioChain, 1);
2033 chain->chain.playsink = playsink;
2034 chain->chain.raw = raw;
2036 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2038 if (playsink->audio_sink) {
2039 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2040 playsink->audio_sink);
2041 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2043 /* only try fallback if no specific sink was chosen */
2044 if (chain->sink == NULL) {
2045 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2046 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2047 chain->sink = try_element (playsink, elem, TRUE);
2049 if (chain->sink == NULL) {
2050 /* if default sink from config.h is different then try it too */
2051 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2052 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2053 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2054 chain->sink = try_element (playsink, elem, TRUE);
2058 playsink->audio_sink = gst_object_ref (chain->sink);
2060 if (chain->sink == NULL)
2063 chain->chain.bin = gst_bin_new ("abin");
2064 bin = GST_BIN_CAST (chain->chain.bin);
2065 gst_object_ref_sink (bin);
2066 gst_bin_add (bin, chain->sink);
2068 /* we have to add a queue when we need to decouple for the video sink in
2069 * visualisations and for streamsynchronizer */
2070 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2071 chain->queue = gst_element_factory_make ("queue", "aqueue");
2072 if (chain->queue == NULL) {
2073 post_missing_element_message (playsink, "queue");
2074 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2075 (_("Missing element '%s' - check your GStreamer installation."),
2076 "queue"), ("audio playback and visualizations might not work"));
2080 g_object_set (chain->queue, "silent", TRUE, NULL);
2081 gst_bin_add (bin, chain->queue);
2082 prev = head = chain->queue;
2085 /* find ts-offset element */
2086 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2087 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2090 /* check if the sink, or something within the sink, has the volume property.
2091 * If it does we don't need to add a volume element. */
2093 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2096 chain->volume = elem;
2098 g_signal_connect (chain->volume, "notify::volume",
2099 G_CALLBACK (notify_volume_cb), playsink);
2101 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2103 chain->sink_volume = TRUE;
2104 /* if the sink also has a mute property we can use this as well. We'll only
2105 * use the mute property if there is a volume property. We can simulate the
2106 * mute with the volume otherwise. */
2108 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2111 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2112 g_signal_connect (chain->mute, "notify::mute",
2113 G_CALLBACK (notify_mute_cb), playsink);
2115 /* use the sink to control the volume and mute */
2116 if (playsink->volume_changed) {
2117 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2118 playsink->volume_changed = FALSE;
2120 if (playsink->mute_changed) {
2122 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2125 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2127 playsink->mute_changed = FALSE;
2130 /* no volume, we need to add a volume element when we can */
2131 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2132 have_volume = FALSE;
2133 chain->sink_volume = FALSE;
2136 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2137 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2138 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2139 gboolean use_volume =
2140 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2141 GST_DEBUG_OBJECT (playsink,
2142 "creating audioconvert with use-converters %d, use-volume %d",
2143 use_converters, use_volume);
2145 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2146 "use-converters", use_converters, "use-volume", use_volume, NULL);
2147 gst_bin_add (bin, chain->conv);
2149 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2150 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2157 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2158 GstPlaySinkAudioConvert *conv =
2159 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2162 chain->volume = conv->volume;
2165 g_signal_connect (chain->volume, "notify::volume",
2166 G_CALLBACK (notify_volume_cb), playsink);
2168 /* volume also has the mute property */
2169 chain->mute = chain->volume;
2170 g_signal_connect (chain->mute, "notify::mute",
2171 G_CALLBACK (notify_mute_cb), playsink);
2173 /* configure with the latest volume and mute */
2174 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2176 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2182 /* we only have to link to the previous element if we have something in
2183 * front of the sink */
2184 GST_DEBUG_OBJECT (playsink, "linking to sink");
2185 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2186 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2190 /* post a warning if we have no way to configure the volume */
2192 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2193 (_("No volume control found")), ("Volume/mute is not available"));
2196 /* and ghost the sinkpad of the headmost element */
2197 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2198 pad = gst_element_get_static_pad (head, "sink");
2199 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2200 gst_object_unref (pad);
2201 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2208 if (!elem && !playsink->audio_sink) {
2209 post_missing_element_message (playsink, "autoaudiosink");
2210 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2211 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2212 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2213 (_("Both autoaudiosink and %s elements are missing."),
2214 DEFAULT_AUDIOSINK), (NULL));
2216 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2217 (_("The autoaudiosink element is missing.")), (NULL));
2220 if (playsink->audio_sink) {
2221 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2222 (_("Configured audiosink %s is not working."),
2223 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2224 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2225 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2226 (_("Both autoaudiosink and %s elements are not working."),
2227 DEFAULT_AUDIOSINK), (NULL));
2229 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2230 (_("The autoaudiosink element is not working.")), (NULL));
2233 free_chain ((GstPlayChain *) chain);
2238 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2239 (NULL), ("Failed to configure the audio sink."));
2240 /* checking sink made it READY */
2241 gst_element_set_state (chain->sink, GST_STATE_NULL);
2242 /* Remove chain from the bin to allow reuse later */
2243 gst_bin_remove (bin, chain->sink);
2244 free_chain ((GstPlayChain *) chain);
2250 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2253 GstPlayAudioChain *chain;
2254 GstStateChangeReturn ret;
2256 chain = playsink->audiochain;
2258 chain->chain.raw = raw;
2260 /* if the chain was active we don't do anything */
2261 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2264 /* try to set the sink element to READY again */
2265 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2266 if (ret == GST_STATE_CHANGE_FAILURE)
2269 /* find ts-offset element */
2270 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2271 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2274 /* check if the sink, or something within the sink, has the volume property.
2275 * If it does we don't need to add a volume element. */
2277 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2280 chain->volume = elem;
2282 if (playsink->volume_changed) {
2283 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2285 /* use the sink to control the volume */
2286 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2287 playsink->volume_changed = FALSE;
2290 g_signal_connect (chain->volume, "notify::volume",
2291 G_CALLBACK (notify_volume_cb), playsink);
2292 /* if the sink also has a mute property we can use this as well. We'll only
2293 * use the mute property if there is a volume property. We can simulate the
2294 * mute with the volume otherwise. */
2296 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2299 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2300 g_signal_connect (chain->mute, "notify::mute",
2301 G_CALLBACK (notify_mute_cb), playsink);
2304 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2306 GstPlaySinkAudioConvert *conv =
2307 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2309 /* no volume, we need to add a volume element when we can */
2310 g_object_set (chain->conv, "use-volume",
2311 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2312 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2314 /* Disconnect signals */
2315 disconnect_chain (chain, playsink);
2317 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2318 chain->volume = conv->volume;
2319 chain->mute = chain->volume;
2321 g_signal_connect (chain->volume, "notify::volume",
2322 G_CALLBACK (notify_volume_cb), playsink);
2324 g_signal_connect (chain->mute, "notify::mute",
2325 G_CALLBACK (notify_mute_cb), playsink);
2327 /* configure with the latest volume and mute */
2328 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2329 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2332 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2338 * +-------------------------------------------------------------------+
2340 * | +----------+ +------------+ +----------+ +-------+ |
2341 * | | visqueue | | audioconv | | audiores | | vis | |
2342 * | +-sink src-sink + samp src-sink src-sink src-+ |
2343 * | | +----------+ +------------+ +----------+ +-------+ | |
2345 * +-------------------------------------------------------------------+
2348 static GstPlayVisChain *
2349 gen_vis_chain (GstPlaySink * playsink)
2351 GstPlayVisChain *chain;
2357 chain = g_new0 (GstPlayVisChain, 1);
2358 chain->chain.playsink = playsink;
2360 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2362 chain->chain.bin = gst_bin_new ("visbin");
2363 bin = GST_BIN_CAST (chain->chain.bin);
2364 gst_object_ref_sink (bin);
2366 /* we're queuing raw audio here, we can remove this queue when we can disable
2367 * async behaviour in the video sink. */
2368 chain->queue = gst_element_factory_make ("queue", "visqueue");
2369 if (chain->queue == NULL)
2371 g_object_set (chain->queue, "silent", TRUE, NULL);
2372 gst_bin_add (bin, chain->queue);
2374 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2375 if (chain->conv == NULL)
2376 goto no_audioconvert;
2377 gst_bin_add (bin, chain->conv);
2379 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2380 if (chain->resample == NULL)
2381 goto no_audioresample;
2382 gst_bin_add (bin, chain->resample);
2384 /* this pad will be used for blocking the dataflow and switching the vis
2386 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2388 if (playsink->visualisation) {
2389 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2390 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2392 if (chain->vis == NULL) {
2393 GST_DEBUG_OBJECT (playsink, "trying goom");
2394 elem = gst_element_factory_make ("goom", "vis");
2395 chain->vis = try_element (playsink, elem, TRUE);
2397 if (chain->vis == NULL)
2400 gst_bin_add (bin, chain->vis);
2402 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2403 GST_PAD_LINK_CHECK_NOTHING);
2405 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2406 GST_PAD_LINK_CHECK_NOTHING);
2408 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2409 GST_PAD_LINK_CHECK_NOTHING);
2413 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2414 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2416 pad = gst_element_get_static_pad (chain->queue, "sink");
2417 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2418 gst_object_unref (pad);
2419 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2421 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2422 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2429 post_missing_element_message (playsink, "queue");
2430 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2431 (_("Missing element '%s' - check your GStreamer installation."),
2433 free_chain ((GstPlayChain *) chain);
2438 post_missing_element_message (playsink, "audioconvert");
2439 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2440 (_("Missing element '%s' - check your GStreamer installation."),
2441 "audioconvert"), ("possibly a liboil version mismatch?"));
2442 free_chain ((GstPlayChain *) chain);
2447 post_missing_element_message (playsink, "audioresample");
2448 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2449 (_("Missing element '%s' - check your GStreamer installation."),
2450 "audioresample"), (NULL));
2451 free_chain ((GstPlayChain *) chain);
2456 post_missing_element_message (playsink, "goom");
2457 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2458 (_("Missing element '%s' - check your GStreamer installation."),
2460 free_chain ((GstPlayChain *) chain);
2465 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2466 (NULL), ("Failed to configure the visualisation element."));
2467 /* element made it to READY */
2468 gst_element_set_state (chain->vis, GST_STATE_NULL);
2469 free_chain ((GstPlayChain *) chain);
2474 /* this function is called when all the request pads are requested and when we
2475 * have to construct the final pipeline. Based on the flags we construct the
2476 * final output pipelines.
2479 gst_play_sink_reconfigure (GstPlaySink * playsink)
2482 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2484 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2486 /* assume we need nothing */
2487 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2489 GST_PLAY_SINK_LOCK (playsink);
2490 GST_OBJECT_LOCK (playsink);
2491 /* get flags, there are protected with the object lock */
2492 flags = playsink->flags;
2493 GST_OBJECT_UNLOCK (playsink);
2495 /* figure out which components we need */
2496 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2497 /* we have subtitles and we are requested to show it */
2501 GST_OBJECT_LOCK (playsink);
2502 if (playsink->overlay_element)
2503 gst_object_unref (playsink->overlay_element);
2504 playsink->overlay_element = NULL;
2506 if (playsink->colorbalance_element) {
2507 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2508 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2509 gst_object_unref (playsink->colorbalance_element);
2511 playsink->colorbalance_element = NULL;
2512 GST_OBJECT_UNLOCK (playsink);
2514 if (((flags & GST_PLAY_FLAG_VIDEO)
2515 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2516 /* we have video and we are requested to show it */
2519 /* we only deinterlace if native video is not requested and
2520 * we have raw video */
2521 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2522 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2523 need_deinterlace = TRUE;
2526 if (playsink->audio_pad) {
2527 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2530 if (playsink->audio_pad_raw) {
2531 /* only can do vis with raw uncompressed audio */
2532 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2533 /* also add video when we add visualisation */
2540 /* we have a text_pad and we need text rendering, in this case we need a
2541 * video_pad to combine the video with the text or visualizations */
2542 if (need_text && !need_video) {
2543 if (playsink->video_pad) {
2545 } else if (need_audio) {
2546 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2547 (_("Can't play a text file without video or visualizations.")),
2548 ("Have text pad but no video pad or visualizations"));
2551 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2552 (_("Can't play a text file without video or visualizations.")),
2553 ("Have text pad but no video pad or visualizations"));
2554 GST_PLAY_SINK_UNLOCK (playsink);
2559 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2560 need_video, need_vis, need_text);
2562 /* set up video pipeline */
2564 gboolean raw, async;
2566 /* we need a raw sink when we do vis or when we have a raw pad */
2567 raw = need_vis ? TRUE : playsink->video_pad_raw;
2568 /* we try to set the sink async=FALSE when we need vis, this way we can
2569 * avoid a queue in the audio chain. */
2572 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2573 playsink->video_pad_raw);
2575 if (playsink->videochain) {
2576 /* try to reactivate the chain */
2577 if (!setup_video_chain (playsink, raw, async)) {
2578 if (playsink->video_sinkpad_stream_synchronizer) {
2579 gst_element_release_request_pad (GST_ELEMENT_CAST
2580 (playsink->stream_synchronizer),
2581 playsink->video_sinkpad_stream_synchronizer);
2582 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2583 playsink->video_sinkpad_stream_synchronizer = NULL;
2584 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2585 playsink->video_srcpad_stream_synchronizer = NULL;
2588 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2590 /* Remove the sink from the bin to keep its state
2591 * and unparent it to allow reuse */
2592 if (playsink->videochain->sink)
2593 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2594 playsink->videochain->sink);
2596 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2597 free_chain ((GstPlayChain *) playsink->videochain);
2598 playsink->videochain = NULL;
2602 if (!playsink->videochain)
2603 playsink->videochain = gen_video_chain (playsink, raw, async);
2604 if (!playsink->videochain)
2607 if (!playsink->video_sinkpad_stream_synchronizer) {
2608 GValue item = { 0, };
2611 playsink->video_sinkpad_stream_synchronizer =
2612 gst_element_get_request_pad (GST_ELEMENT_CAST
2613 (playsink->stream_synchronizer), "sink_%u");
2614 it = gst_pad_iterate_internal_links
2615 (playsink->video_sinkpad_stream_synchronizer);
2617 gst_iterator_next (it, &item);
2618 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2619 g_value_unset (&item);
2620 g_assert (playsink->video_srcpad_stream_synchronizer);
2621 gst_iterator_free (it);
2624 if (playsink->video_pad)
2625 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2626 playsink->video_sinkpad_stream_synchronizer);
2628 if (need_deinterlace) {
2629 if (!playsink->videodeinterlacechain)
2630 playsink->videodeinterlacechain =
2631 gen_video_deinterlace_chain (playsink);
2632 if (!playsink->videodeinterlacechain)
2635 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2637 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2639 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2640 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2642 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2643 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2645 if (playsink->videodeinterlacechain) {
2646 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2647 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2652 GST_DEBUG_OBJECT (playsink, "adding video chain");
2653 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2654 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2655 /* if we are not part of vis or subtitles, set the ghostpad target */
2656 if (!need_vis && !need_text && (!playsink->textchain
2657 || !playsink->text_pad)) {
2658 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2659 if (need_deinterlace)
2660 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2661 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2663 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2664 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2667 GST_DEBUG_OBJECT (playsink, "no video needed");
2668 if (playsink->videochain) {
2669 GST_DEBUG_OBJECT (playsink, "removing video chain");
2670 if (playsink->vischain) {
2673 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2675 /* also had visualisation, release the tee srcpad before we then
2676 * unlink the video from it */
2677 if (playsink->audio_tee_vissrc) {
2678 gst_element_release_request_pad (playsink->audio_tee,
2679 playsink->audio_tee_vissrc);
2680 gst_object_unref (playsink->audio_tee_vissrc);
2681 playsink->audio_tee_vissrc = NULL;
2684 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2685 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2688 if (playsink->video_sinkpad_stream_synchronizer) {
2689 gst_element_release_request_pad (GST_ELEMENT_CAST
2690 (playsink->stream_synchronizer),
2691 playsink->video_sinkpad_stream_synchronizer);
2692 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2693 playsink->video_sinkpad_stream_synchronizer = NULL;
2694 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2695 playsink->video_srcpad_stream_synchronizer = NULL;
2698 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2699 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2700 if (playsink->videochain->ts_offset)
2701 gst_object_unref (playsink->videochain->ts_offset);
2702 playsink->videochain->ts_offset = NULL;
2705 if (playsink->videodeinterlacechain) {
2706 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2707 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2710 if (playsink->video_pad)
2711 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2717 GST_DEBUG_OBJECT (playsink, "adding audio");
2719 /* get a raw sink if we are asked for a raw pad */
2720 raw = playsink->audio_pad_raw;
2722 if (playsink->audiochain) {
2723 /* try to reactivate the chain */
2724 if (!setup_audio_chain (playsink, raw)) {
2725 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2726 if (playsink->audio_tee_asrc) {
2727 gst_element_release_request_pad (playsink->audio_tee,
2728 playsink->audio_tee_asrc);
2729 gst_object_unref (playsink->audio_tee_asrc);
2730 playsink->audio_tee_asrc = NULL;
2733 if (playsink->audio_sinkpad_stream_synchronizer) {
2734 gst_element_release_request_pad (GST_ELEMENT_CAST
2735 (playsink->stream_synchronizer),
2736 playsink->audio_sinkpad_stream_synchronizer);
2737 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2738 playsink->audio_sinkpad_stream_synchronizer = NULL;
2739 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2740 playsink->audio_srcpad_stream_synchronizer = NULL;
2743 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2745 /* Remove the sink from the bin to keep its state
2746 * and unparent it to allow reuse */
2747 if (playsink->audiochain->sink)
2748 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2749 playsink->audiochain->sink);
2751 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2752 disconnect_chain (playsink->audiochain, playsink);
2753 playsink->audiochain->volume = NULL;
2754 playsink->audiochain->mute = NULL;
2755 if (playsink->audiochain->ts_offset)
2756 gst_object_unref (playsink->audiochain->ts_offset);
2757 playsink->audiochain->ts_offset = NULL;
2758 free_chain ((GstPlayChain *) playsink->audiochain);
2759 playsink->audiochain = NULL;
2760 playsink->volume_changed = playsink->mute_changed = FALSE;
2764 if (!playsink->audiochain) {
2765 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2766 playsink->audiochain = gen_audio_chain (playsink, raw);
2769 if (!playsink->audio_sinkpad_stream_synchronizer) {
2770 GValue item = { 0, };
2773 playsink->audio_sinkpad_stream_synchronizer =
2774 gst_element_get_request_pad (GST_ELEMENT_CAST
2775 (playsink->stream_synchronizer), "sink_%u");
2776 it = gst_pad_iterate_internal_links
2777 (playsink->audio_sinkpad_stream_synchronizer);
2779 gst_iterator_next (it, &item);
2780 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
2781 g_value_unset (&item);
2782 g_assert (playsink->audio_srcpad_stream_synchronizer);
2783 gst_iterator_free (it);
2786 if (playsink->audiochain) {
2787 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2788 if (playsink->audio_tee_asrc == NULL) {
2789 playsink->audio_tee_asrc =
2790 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2792 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2793 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2794 gst_pad_link_full (playsink->audio_tee_asrc,
2795 playsink->audio_sinkpad_stream_synchronizer,
2796 GST_PAD_LINK_CHECK_NOTHING);
2797 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2798 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2801 GST_DEBUG_OBJECT (playsink, "no audio needed");
2802 /* we have no audio or we are requested to not play audio */
2803 if (playsink->audiochain) {
2804 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2805 /* release the audio pad */
2806 if (playsink->audio_tee_asrc) {
2807 gst_element_release_request_pad (playsink->audio_tee,
2808 playsink->audio_tee_asrc);
2809 gst_object_unref (playsink->audio_tee_asrc);
2810 playsink->audio_tee_asrc = NULL;
2813 if (playsink->audio_sinkpad_stream_synchronizer) {
2814 gst_element_release_request_pad (GST_ELEMENT_CAST
2815 (playsink->stream_synchronizer),
2816 playsink->audio_sinkpad_stream_synchronizer);
2817 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2818 playsink->audio_sinkpad_stream_synchronizer = NULL;
2819 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2820 playsink->audio_srcpad_stream_synchronizer = NULL;
2823 if (playsink->audiochain->sink_volume) {
2824 disconnect_chain (playsink->audiochain, playsink);
2825 playsink->audiochain->volume = NULL;
2826 playsink->audiochain->mute = NULL;
2827 if (playsink->audiochain->ts_offset)
2828 gst_object_unref (playsink->audiochain->ts_offset);
2829 playsink->audiochain->ts_offset = NULL;
2831 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2832 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2839 if (!playsink->vischain)
2840 playsink->vischain = gen_vis_chain (playsink);
2842 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2844 if (playsink->vischain) {
2845 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2847 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2848 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2849 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2850 if (playsink->audio_tee_vissrc == NULL) {
2851 playsink->audio_tee_vissrc =
2852 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2854 gst_pad_link_full (playsink->audio_tee_vissrc,
2855 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2856 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2857 GST_PAD_LINK_CHECK_NOTHING);
2858 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2859 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2860 gst_object_unref (srcpad);
2863 GST_DEBUG_OBJECT (playsink, "no vis needed");
2864 if (playsink->vischain) {
2865 if (playsink->audio_tee_vissrc) {
2866 gst_element_release_request_pad (playsink->audio_tee,
2867 playsink->audio_tee_vissrc);
2868 gst_object_unref (playsink->audio_tee_vissrc);
2869 playsink->audio_tee_vissrc = NULL;
2871 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2872 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2873 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2878 GST_DEBUG_OBJECT (playsink, "adding text");
2879 if (!playsink->textchain) {
2880 GST_DEBUG_OBJECT (playsink, "creating text chain");
2881 playsink->textchain = gen_text_chain (playsink);
2883 if (playsink->textchain) {
2886 GST_DEBUG_OBJECT (playsink, "adding text chain");
2887 if (playsink->textchain->overlay)
2888 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2890 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2892 if (!playsink->text_sinkpad_stream_synchronizer) {
2893 GValue item = { 0, };
2895 playsink->text_sinkpad_stream_synchronizer =
2896 gst_element_get_request_pad (GST_ELEMENT_CAST
2897 (playsink->stream_synchronizer), "sink_%u");
2898 it = gst_pad_iterate_internal_links
2899 (playsink->text_sinkpad_stream_synchronizer);
2901 gst_iterator_next (it, &item);
2902 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
2903 g_value_unset (&item);
2904 g_assert (playsink->text_srcpad_stream_synchronizer);
2905 gst_iterator_free (it);
2907 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2908 playsink->text_sinkpad_stream_synchronizer);
2909 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2910 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2917 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2918 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2919 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2920 GST_PAD_LINK_CHECK_NOTHING);
2921 gst_object_unref (srcpad);
2923 if (need_deinterlace)
2924 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2925 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2927 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2928 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2930 gst_pad_link_full (playsink->textchain->srcpad,
2931 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2933 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2936 GST_DEBUG_OBJECT (playsink, "no text needed");
2937 /* we have no subtitles/text or we are requested to not show them */
2939 if (playsink->text_sinkpad_stream_synchronizer) {
2940 gst_element_release_request_pad (GST_ELEMENT_CAST
2941 (playsink->stream_synchronizer),
2942 playsink->text_sinkpad_stream_synchronizer);
2943 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2944 playsink->text_sinkpad_stream_synchronizer = NULL;
2945 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2946 playsink->text_srcpad_stream_synchronizer = NULL;
2949 if (playsink->textchain) {
2950 if (playsink->text_pad == NULL) {
2951 /* no text pad, remove the chain entirely */
2952 GST_DEBUG_OBJECT (playsink, "removing text chain");
2953 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2954 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2956 /* we have a chain and a textpad, turn the subtitles off */
2957 GST_DEBUG_OBJECT (playsink, "turning off the text");
2958 if (playsink->textchain->overlay)
2959 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2963 if (!need_video && playsink->video_pad) {
2964 if (playsink->video_sinkpad_stream_synchronizer) {
2965 gst_element_release_request_pad (GST_ELEMENT_CAST
2966 (playsink->stream_synchronizer),
2967 playsink->video_sinkpad_stream_synchronizer);
2968 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2969 playsink->video_sinkpad_stream_synchronizer = NULL;
2970 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2971 playsink->video_srcpad_stream_synchronizer = NULL;
2974 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2977 if (playsink->text_pad && !playsink->textchain)
2978 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2980 update_av_offset (playsink);
2981 do_async_done (playsink);
2982 GST_PLAY_SINK_UNLOCK (playsink);
2989 /* gen_ chain already posted error */
2990 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2991 GST_PLAY_SINK_UNLOCK (playsink);
2997 * gst_play_sink_set_flags:
2998 * @playsink: a #GstPlaySink
2999 * @flags: #GstPlayFlags
3001 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3002 * when constructing the sink pipelins.
3004 * Returns: TRUE if the flags could be configured.
3007 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3009 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3011 GST_OBJECT_LOCK (playsink);
3012 playsink->flags = flags;
3013 GST_OBJECT_UNLOCK (playsink);
3019 * gst_play_sink_get_flags:
3020 * @playsink: a #GstPlaySink
3022 * Get the flags of @playsink. That flags control the behaviour of the sink when
3023 * it constructs the sink pipelines.
3025 * Returns: the currently configured #GstPlayFlags.
3028 gst_play_sink_get_flags (GstPlaySink * playsink)
3032 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3034 GST_OBJECT_LOCK (playsink);
3035 res = playsink->flags;
3036 GST_OBJECT_UNLOCK (playsink);
3042 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3044 GstPlayTextChain *chain;
3046 GST_PLAY_SINK_LOCK (playsink);
3047 chain = (GstPlayTextChain *) playsink->textchain;
3048 g_free (playsink->font_desc);
3049 playsink->font_desc = g_strdup (desc);
3050 if (chain && chain->overlay) {
3051 g_object_set (chain->overlay, "font-desc", desc, NULL);
3053 GST_PLAY_SINK_UNLOCK (playsink);
3057 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3059 gchar *result = NULL;
3060 GstPlayTextChain *chain;
3062 GST_PLAY_SINK_LOCK (playsink);
3063 chain = (GstPlayTextChain *) playsink->textchain;
3064 if (chain && chain->overlay) {
3065 g_object_get (chain->overlay, "font-desc", &result, NULL);
3066 playsink->font_desc = g_strdup (result);
3068 result = g_strdup (playsink->font_desc);
3070 GST_PLAY_SINK_UNLOCK (playsink);
3076 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3077 const gchar * encoding)
3079 GstPlayTextChain *chain;
3081 GST_PLAY_SINK_LOCK (playsink);
3082 chain = (GstPlayTextChain *) playsink->textchain;
3083 g_free (playsink->subtitle_encoding);
3084 playsink->subtitle_encoding = g_strdup (encoding);
3085 if (chain && chain->overlay) {
3086 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3088 GST_PLAY_SINK_UNLOCK (playsink);
3092 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3094 gchar *result = NULL;
3095 GstPlayTextChain *chain;
3097 GST_PLAY_SINK_LOCK (playsink);
3098 chain = (GstPlayTextChain *) playsink->textchain;
3099 if (chain && chain->overlay) {
3100 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3101 playsink->subtitle_encoding = g_strdup (result);
3103 result = g_strdup (playsink->subtitle_encoding);
3105 GST_PLAY_SINK_UNLOCK (playsink);
3111 update_av_offset (GstPlaySink * playsink)
3114 GstPlayAudioChain *achain;
3115 GstPlayVideoChain *vchain;
3117 av_offset = playsink->av_offset;
3118 achain = (GstPlayAudioChain *) playsink->audiochain;
3119 vchain = (GstPlayVideoChain *) playsink->videochain;
3121 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3122 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3123 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3125 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3130 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3132 GST_PLAY_SINK_LOCK (playsink);
3133 playsink->av_offset = av_offset;
3134 update_av_offset (playsink);
3135 GST_PLAY_SINK_UNLOCK (playsink);
3139 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3143 GST_PLAY_SINK_LOCK (playsink);
3144 result = playsink->av_offset;
3145 GST_PLAY_SINK_UNLOCK (playsink);
3151 * gst_play_sink_get_last_sample:
3152 * @playsink: a #GstPlaySink
3154 * Get the last displayed sample from @playsink. This sample is in the native
3155 * format of the sink element, the caps in the result sample contain the format
3156 * of the frame data.
3158 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3162 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3164 GstSample *result = NULL;
3165 GstPlayVideoChain *chain;
3167 GST_PLAY_SINK_LOCK (playsink);
3168 GST_DEBUG_OBJECT (playsink, "taking last sample");
3169 /* get the video chain if we can */
3170 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3171 GST_DEBUG_OBJECT (playsink, "found video chain");
3172 /* see if the chain is active */
3173 if (chain->chain.activated && chain->sink) {
3176 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3178 /* find and get the last-buffer property now */
3180 gst_play_sink_find_property (playsink, chain->sink,
3181 "last-sample", GST_TYPE_SAMPLE))) {
3182 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3183 g_object_get (elem, "last-sample", &result, NULL);
3184 gst_object_unref (elem);
3188 GST_PLAY_SINK_UNLOCK (playsink);
3194 * gst_play_sink_convert_sample:
3195 * @playsink: a #GstPlaySink
3198 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3199 * be in the native format of the sink element and the caps on the buffer
3200 * describe the format of the frame. If @caps is not %NULL, the video
3201 * frame will be converted to the format of the caps.
3203 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3204 * available or when the conversion failed.
3207 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3212 result = gst_play_sink_get_last_sample (playsink);
3213 if (result != NULL && caps != NULL) {
3216 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3217 if (temp == NULL && err)
3220 gst_sample_unref (result);
3228 /* I'm really uncertain whether we should make playsink post an error
3229 * on the bus or not. It's not like it's a critical issue regarding
3230 * playsink behaviour. */
3231 GST_ERROR ("Error converting frame: %s", err->message);
3232 gst_sample_unref (result);
3239 is_raw_structure (GstStructure * s)
3243 name = gst_structure_get_name (s);
3245 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3251 is_raw_pad (GstPad * pad)
3253 GstPad *peer = gst_pad_get_peer (pad);
3255 gboolean raw = TRUE;
3260 caps = gst_pad_get_current_caps (peer);
3264 caps = gst_pad_query_caps (peer, NULL);
3266 n = gst_caps_get_size (caps);
3267 for (i = 0; i < n; i++) {
3268 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3272 } else if (raw != r) {
3273 GST_ERROR_OBJECT (pad,
3274 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3280 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3282 gst_caps_unref (caps);
3283 gst_object_unref (peer);
3288 static GstPadProbeReturn
3289 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3290 gpointer user_data);
3293 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3295 if (playsink->video_pad) {
3297 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3298 (playsink->video_pad)));
3299 if (blocked && playsink->video_block_id == 0) {
3300 playsink->video_block_id =
3301 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3302 sinkpad_blocked_cb, gst_object_ref (playsink),
3303 (GDestroyNotify) gst_object_unref);
3304 } else if (!blocked && playsink->video_block_id) {
3305 gst_pad_remove_probe (opad, playsink->video_block_id);
3306 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3307 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3308 playsink->video_block_id = 0;
3309 playsink->video_pad_blocked = FALSE;
3311 gst_object_unref (opad);
3316 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3318 if (playsink->audio_pad) {
3320 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3321 (playsink->audio_pad)));
3322 if (blocked && playsink->audio_block_id == 0) {
3323 playsink->audio_block_id =
3324 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3325 sinkpad_blocked_cb, gst_object_ref (playsink),
3326 (GDestroyNotify) gst_object_unref);
3327 } else if (!blocked && playsink->audio_block_id) {
3328 gst_pad_remove_probe (opad, playsink->audio_block_id);
3329 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3330 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3331 playsink->audio_block_id = 0;
3332 playsink->audio_pad_blocked = FALSE;
3334 gst_object_unref (opad);
3339 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3341 if (playsink->text_pad) {
3343 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3344 (playsink->text_pad)));
3345 if (blocked && playsink->text_block_id == 0) {
3346 playsink->text_block_id =
3347 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3348 sinkpad_blocked_cb, gst_object_ref (playsink),
3349 (GDestroyNotify) gst_object_unref);
3350 } else if (!blocked && playsink->text_block_id) {
3351 gst_pad_remove_probe (opad, playsink->text_block_id);
3352 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3353 playsink->text_block_id = 0;
3354 playsink->text_pad_blocked = FALSE;
3356 gst_object_unref (opad);
3360 static GstPadProbeReturn
3361 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3364 GstPlaySink *playsink = (GstPlaySink *) user_data;
3367 GST_PLAY_SINK_LOCK (playsink);
3369 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3370 if (pad == playsink->video_pad) {
3371 playsink->video_pad_blocked = TRUE;
3372 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3373 } else if (pad == playsink->audio_pad) {
3374 playsink->audio_pad_blocked = TRUE;
3375 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3376 } else if (pad == playsink->text_pad) {
3377 playsink->text_pad_blocked = TRUE;
3378 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3381 /* We reconfigure when for ALL streams:
3382 * * there isn't a pad
3383 * * OR the pad is blocked
3384 * * OR there are no pending blocks on that pad
3387 if ((!playsink->video_pad || playsink->video_pad_blocked
3388 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3389 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3390 && (!playsink->text_pad || playsink->text_pad_blocked
3391 || !PENDING_TEXT_BLOCK (playsink))) {
3392 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3394 if (playsink->video_pad) {
3395 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3396 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3397 playsink->video_pad_raw);
3400 if (playsink->audio_pad) {
3401 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3402 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3403 playsink->audio_pad_raw);
3406 gst_play_sink_reconfigure (playsink);
3408 video_set_blocked (playsink, FALSE);
3409 audio_set_blocked (playsink, FALSE);
3410 text_set_blocked (playsink, FALSE);
3413 gst_object_unref (pad);
3415 GST_PLAY_SINK_UNLOCK (playsink);
3417 return GST_PAD_PROBE_OK;
3421 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3423 gboolean reconfigure = FALSE;
3427 g_object_get (pad, "caps", &caps, NULL);
3431 if (pad == playsink->audio_pad) {
3432 raw = is_raw_pad (pad);
3433 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3434 && playsink->audiochain;
3435 GST_DEBUG_OBJECT (pad,
3436 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3438 } else if (pad == playsink->video_pad) {
3439 raw = is_raw_pad (pad);
3440 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3441 && playsink->videochain;
3442 GST_DEBUG_OBJECT (pad,
3443 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3447 gst_caps_unref (caps);
3450 GST_PLAY_SINK_LOCK (playsink);
3451 video_set_blocked (playsink, TRUE);
3452 audio_set_blocked (playsink, TRUE);
3453 text_set_blocked (playsink, TRUE);
3454 GST_PLAY_SINK_UNLOCK (playsink);
3459 * gst_play_sink_request_pad
3460 * @playsink: a #GstPlaySink
3461 * @type: a #GstPlaySinkType
3463 * Create or return a pad of @type.
3465 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3468 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3471 gboolean created = FALSE;
3472 gboolean activate = TRUE;
3473 const gchar *pad_name = NULL;
3474 gulong *block_id = NULL;
3476 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3478 GST_PLAY_SINK_LOCK (playsink);
3480 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3481 case GST_PLAY_SINK_TYPE_AUDIO:
3482 pad_name = "audio_sink";
3483 if (!playsink->audio_tee) {
3484 GST_LOG_OBJECT (playsink, "creating tee");
3485 /* create tee when needed. This element will feed the audio sink chain
3486 * and the vis chain. */
3487 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3488 if (playsink->audio_tee == NULL) {
3489 post_missing_element_message (playsink, "tee");
3490 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3491 (_("Missing element '%s' - check your GStreamer installation."),
3496 playsink->audio_tee_sink =
3497 gst_element_get_static_pad (playsink->audio_tee, "sink");
3498 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3499 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3502 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3504 if (!playsink->audio_pad) {
3505 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3506 playsink->audio_pad =
3507 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3508 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3509 G_CALLBACK (caps_notify_cb), playsink);
3512 playsink->audio_pad_raw = FALSE;
3513 res = playsink->audio_pad;
3514 block_id = &playsink->audio_block_id;
3516 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3517 case GST_PLAY_SINK_TYPE_VIDEO:
3518 pad_name = "video_sink";
3519 if (!playsink->video_pad) {
3520 GST_LOG_OBJECT (playsink, "ghosting videosink");
3521 playsink->video_pad =
3522 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3523 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3524 G_CALLBACK (caps_notify_cb), playsink);
3527 playsink->video_pad_raw = FALSE;
3528 res = playsink->video_pad;
3529 block_id = &playsink->video_block_id;
3531 case GST_PLAY_SINK_TYPE_TEXT:
3532 GST_LOG_OBJECT (playsink, "ghosting text");
3533 if (!playsink->text_pad) {
3534 playsink->text_pad =
3535 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3538 res = playsink->text_pad;
3539 block_id = &playsink->text_block_id;
3541 case GST_PLAY_SINK_TYPE_FLUSHING:
3545 /* we need a unique padname for the flushing pad. */
3546 padname = g_strdup_printf ("flushing_%u", playsink->count);
3547 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3558 GST_PLAY_SINK_UNLOCK (playsink);
3560 if (created && res) {
3561 /* we have to add the pad when it's active or we get an error when the
3562 * element is 'running' */
3563 gst_pad_set_active (res, TRUE);
3564 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3565 if (block_id && *block_id == 0) {
3567 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3570 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3571 sinkpad_blocked_cb, gst_object_ref (playsink),
3572 (GDestroyNotify) gst_object_unref);
3573 PENDING_FLAG_SET (playsink, type);
3574 gst_object_unref (blockpad);
3577 gst_pad_set_active (res, activate);
3584 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3585 const gchar * name, const GstCaps * caps)
3589 GstPlaySinkType type;
3590 const gchar *tplname;
3592 g_return_val_if_fail (templ != NULL, NULL);
3594 GST_DEBUG_OBJECT (element, "name:%s", name);
3596 psink = GST_PLAY_SINK (element);
3597 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3599 /* Figure out the GstPlaySinkType based on the template */
3600 if (!strcmp (tplname, "audio_sink"))
3601 type = GST_PLAY_SINK_TYPE_AUDIO;
3602 else if (!strcmp (tplname, "audio_raw_sink"))
3603 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3604 else if (!strcmp (tplname, "video_sink"))
3605 type = GST_PLAY_SINK_TYPE_VIDEO;
3606 else if (!strcmp (tplname, "video_raw_sink"))
3607 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3608 else if (!strcmp (tplname, "text_sink"))
3609 type = GST_PLAY_SINK_TYPE_TEXT;
3611 goto unknown_template;
3613 pad = gst_play_sink_request_pad (psink, type);
3617 GST_WARNING_OBJECT (element, "Unknown pad template");
3622 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3624 GstPad **res = NULL;
3625 gboolean untarget = TRUE;
3627 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3629 GST_PLAY_SINK_LOCK (playsink);
3630 if (pad == playsink->video_pad) {
3631 res = &playsink->video_pad;
3632 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3634 } else if (pad == playsink->audio_pad) {
3635 res = &playsink->audio_pad;
3636 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3638 } else if (pad == playsink->text_pad) {
3639 res = &playsink->text_pad;
3641 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3645 GST_PLAY_SINK_UNLOCK (playsink);
3648 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3649 gst_pad_set_active (*res, FALSE);
3651 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3652 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3654 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3655 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3661 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3663 GstPlaySink *psink = GST_PLAY_SINK (element);
3665 gst_play_sink_release_pad (psink, pad);
3669 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3671 GstPlaySink *playsink;
3673 playsink = GST_PLAY_SINK_CAST (bin);
3675 switch (GST_MESSAGE_TYPE (message)) {
3676 case GST_MESSAGE_STEP_DONE:
3681 gboolean flush, intermediate, eos;
3684 GST_INFO_OBJECT (playsink, "Handling step-done message");
3685 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3686 &intermediate, &duration, &eos);
3688 if (format == GST_FORMAT_BUFFERS) {
3689 /* for the buffer format, we align the other streams */
3690 if (playsink->audiochain) {
3694 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3697 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3698 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3702 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3705 case GST_MESSAGE_ELEMENT:{
3706 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
3707 GstVideoOverlay *overlay;
3709 GST_OBJECT_LOCK (playsink);
3710 if (playsink->overlay_element
3711 && GST_OBJECT_CAST (playsink->overlay_element) !=
3712 GST_MESSAGE_SRC (message)) {
3713 gst_object_unref (playsink->overlay_element);
3714 playsink->overlay_element = NULL;
3717 if (!playsink->overlay_element)
3718 playsink->overlay_element =
3719 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
3721 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
3722 GST_OBJECT_UNLOCK (playsink);
3724 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
3726 if (playsink->overlay_handle_set)
3727 gst_video_overlay_set_window_handle (playsink->overlay_element,
3728 playsink->overlay_handle);
3729 if (playsink->overlay_handle_events_set)
3730 gst_video_overlay_handle_events (playsink->overlay_element,
3731 playsink->overlay_handle_events);
3732 if (playsink->overlay_render_rectangle_set)
3733 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
3734 playsink->overlay_x, playsink->overlay_y,
3735 playsink->overlay_width, playsink->overlay_height);
3737 gst_object_unref (overlay);
3738 gst_message_unref (message);
3739 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
3744 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3749 /* Send an event to our sinks until one of them works; don't then send to the
3750 * remaining sinks (unlike GstBin)
3751 * Special case: If a text sink is set we need to send the event
3752 * to them in case it's source is different from the a/v stream's source.
3755 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3757 gboolean res = TRUE;
3758 if (playsink->textchain && playsink->textchain->sink) {
3759 gst_event_ref (event);
3760 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3761 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3763 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3767 if (playsink->videochain) {
3768 gst_event_ref (event);
3769 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3770 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3773 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3775 if (playsink->audiochain) {
3776 gst_event_ref (event);
3777 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3778 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3781 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3785 gst_event_unref (event);
3789 /* We only want to send the event to a single sink (overriding GstBin's
3790 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3791 * events appropriately. So, this is a messy duplication of code. */
3793 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3795 gboolean res = FALSE;
3796 GstEventType event_type = GST_EVENT_TYPE (event);
3797 GstPlaySink *playsink;
3798 playsink = GST_PLAY_SINK_CAST (element);
3799 switch (event_type) {
3800 case GST_EVENT_SEEK:
3801 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3802 res = gst_play_sink_send_event_to_sink (playsink, event);
3804 case GST_EVENT_STEP:
3809 gboolean flush, intermediate;
3810 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3812 if (format == GST_FORMAT_BUFFERS) {
3813 /* for buffers, we will try to step video frames, for other formats we
3814 * send the step to all sinks */
3815 res = gst_play_sink_send_event_to_sink (playsink, event);
3818 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3825 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3832 static GstStateChangeReturn
3833 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3835 GstStateChangeReturn ret;
3836 GstStateChangeReturn bret;
3837 GstPlaySink *playsink;
3838 playsink = GST_PLAY_SINK (element);
3839 switch (transition) {
3840 case GST_STATE_CHANGE_READY_TO_PAUSED:
3841 playsink->need_async_start = TRUE;
3842 /* we want to go async to PAUSED until we managed to configure and add the
3844 do_async_start (playsink);
3845 ret = GST_STATE_CHANGE_ASYNC;
3847 case GST_STATE_CHANGE_PAUSED_TO_READY:
3848 /* unblock all pads here */
3849 GST_PLAY_SINK_LOCK (playsink);
3850 video_set_blocked (playsink, FALSE);
3851 audio_set_blocked (playsink, FALSE);
3852 text_set_blocked (playsink, FALSE);
3853 GST_PLAY_SINK_UNLOCK (playsink);
3855 case GST_STATE_CHANGE_READY_TO_NULL:
3856 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3857 /* remove our links to the mute and volume elements when they were
3858 * provided by a sink */
3859 disconnect_chain (playsink->audiochain, playsink);
3860 playsink->audiochain->volume = NULL;
3861 playsink->audiochain->mute = NULL;
3864 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3865 gst_object_unref (playsink->audiochain->ts_offset);
3866 playsink->audiochain->ts_offset = NULL;
3869 if (playsink->videochain && playsink->videochain->ts_offset) {
3870 gst_object_unref (playsink->videochain->ts_offset);
3871 playsink->videochain->ts_offset = NULL;
3874 GST_OBJECT_LOCK (playsink);
3875 if (playsink->overlay_element)
3876 gst_object_unref (playsink->overlay_element);
3877 playsink->overlay_element = NULL;
3879 if (playsink->colorbalance_element) {
3880 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3881 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3882 gst_object_unref (playsink->colorbalance_element);
3884 playsink->colorbalance_element = NULL;
3885 GST_OBJECT_UNLOCK (playsink);
3887 ret = GST_STATE_CHANGE_SUCCESS;
3890 /* all other state changes return SUCCESS by default, this value can be
3891 * overridden by the result of the children */
3892 ret = GST_STATE_CHANGE_SUCCESS;
3896 /* do the state change of the children */
3898 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3900 /* now look at the result of our children and adjust the return value */
3902 case GST_STATE_CHANGE_FAILURE:
3903 /* failure, we stop */
3904 goto activate_failed;
3905 case GST_STATE_CHANGE_NO_PREROLL:
3906 /* some child returned NO_PREROLL. This is strange but we never know. We
3907 * commit our async state change (if any) and return the NO_PREROLL */
3908 do_async_done (playsink);
3911 case GST_STATE_CHANGE_ASYNC:
3912 /* some child was async, return this */
3916 /* return our previously configured return value */
3920 switch (transition) {
3921 case GST_STATE_CHANGE_READY_TO_PAUSED:
3923 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3924 /* FIXME Release audio device when we implement that */
3925 playsink->need_async_start = TRUE;
3927 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3928 if (playsink->video_sinkpad_stream_synchronizer) {
3929 gst_element_release_request_pad (GST_ELEMENT_CAST
3930 (playsink->stream_synchronizer),
3931 playsink->video_sinkpad_stream_synchronizer);
3932 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3933 playsink->video_sinkpad_stream_synchronizer = NULL;
3934 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3935 playsink->video_srcpad_stream_synchronizer = NULL;
3937 if (playsink->audio_sinkpad_stream_synchronizer) {
3938 gst_element_release_request_pad (GST_ELEMENT_CAST
3939 (playsink->stream_synchronizer),
3940 playsink->audio_sinkpad_stream_synchronizer);
3941 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3942 playsink->audio_sinkpad_stream_synchronizer = NULL;
3943 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3944 playsink->audio_srcpad_stream_synchronizer = NULL;
3946 if (playsink->text_sinkpad_stream_synchronizer) {
3947 gst_element_release_request_pad (GST_ELEMENT_CAST
3948 (playsink->stream_synchronizer),
3949 playsink->text_sinkpad_stream_synchronizer);
3950 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3951 playsink->text_sinkpad_stream_synchronizer = NULL;
3952 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3953 playsink->text_srcpad_stream_synchronizer = NULL;
3957 case GST_STATE_CHANGE_READY_TO_NULL:
3958 /* remove sinks we added */
3959 if (playsink->videodeinterlacechain) {
3960 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3962 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3964 if (playsink->videochain) {
3965 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3966 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3968 if (playsink->audiochain) {
3969 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3970 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3972 if (playsink->vischain) {
3973 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3974 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3976 if (playsink->textchain) {
3977 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3978 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3980 do_async_done (playsink);
3981 /* when going to READY, keep elements around as long as possible,
3982 * so they may be re-used faster next time/url around.
3983 * when really going to NULL, clean up everything completely. */
3984 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3986 /* Unparent the sinks to allow reuse */
3987 if (playsink->videochain && playsink->videochain->sink)
3988 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3989 playsink->videochain->sink);
3990 if (playsink->audiochain && playsink->audiochain->sink)
3991 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3992 playsink->audiochain->sink);
3993 if (playsink->textchain && playsink->textchain->sink)
3994 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3995 playsink->textchain->sink);
3996 if (playsink->audio_sink != NULL)
3997 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3998 if (playsink->video_sink != NULL)
3999 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4000 if (playsink->visualisation != NULL)
4001 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4002 if (playsink->text_sink != NULL)
4003 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4004 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4005 playsink->videodeinterlacechain = NULL;
4006 free_chain ((GstPlayChain *) playsink->videochain);
4007 playsink->videochain = NULL;
4008 free_chain ((GstPlayChain *) playsink->audiochain);
4009 playsink->audiochain = NULL;
4010 free_chain ((GstPlayChain *) playsink->vischain);
4011 playsink->vischain = NULL;
4012 free_chain ((GstPlayChain *) playsink->textchain);
4013 playsink->textchain = NULL;
4023 GST_DEBUG_OBJECT (element,
4024 "element failed to change states -- activation problem?");
4025 return GST_STATE_CHANGE_FAILURE;
4030 gst_play_sink_set_property (GObject * object, guint prop_id,
4031 const GValue * value, GParamSpec * spec)
4033 GstPlaySink *playsink = GST_PLAY_SINK (object);
4036 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4039 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4042 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4044 case PROP_FONT_DESC:
4045 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4047 case PROP_SUBTITLE_ENCODING:
4048 gst_play_sink_set_subtitle_encoding (playsink,
4049 g_value_get_string (value));
4051 case PROP_VIS_PLUGIN:
4052 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4054 case PROP_AV_OFFSET:
4055 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4057 case PROP_VIDEO_SINK:
4058 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4059 g_value_get_object (value));
4061 case PROP_AUDIO_SINK:
4062 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4063 g_value_get_object (value));
4065 case PROP_TEXT_SINK:
4066 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4067 g_value_get_object (value));
4070 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4076 gst_play_sink_get_property (GObject * object, guint prop_id,
4077 GValue * value, GParamSpec * spec)
4079 GstPlaySink *playsink = GST_PLAY_SINK (object);
4082 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4085 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4088 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4090 case PROP_FONT_DESC:
4091 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4093 case PROP_SUBTITLE_ENCODING:
4094 g_value_take_string (value,
4095 gst_play_sink_get_subtitle_encoding (playsink));
4097 case PROP_VIS_PLUGIN:
4098 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4101 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4103 case PROP_AV_OFFSET:
4104 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4106 case PROP_VIDEO_SINK:
4107 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4108 GST_PLAY_SINK_TYPE_VIDEO));
4110 case PROP_AUDIO_SINK:
4111 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4112 GST_PLAY_SINK_TYPE_AUDIO));
4114 case PROP_TEXT_SINK:
4115 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4116 GST_PLAY_SINK_TYPE_TEXT));
4119 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4125 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4127 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4128 GstVideoOverlay *overlay_element;
4130 GST_OBJECT_LOCK (playsink);
4131 if (playsink->overlay_element)
4133 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4135 overlay_element = NULL;
4136 GST_OBJECT_UNLOCK (playsink);
4138 if (overlay_element) {
4139 gst_video_overlay_expose (overlay_element);
4140 gst_object_unref (overlay_element);
4145 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4146 gboolean handle_events)
4148 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4149 GstVideoOverlay *overlay_element;
4151 GST_OBJECT_LOCK (playsink);
4152 if (playsink->overlay_element)
4154 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4156 overlay_element = NULL;
4157 GST_OBJECT_UNLOCK (playsink);
4159 playsink->overlay_handle_events_set = TRUE;
4160 playsink->overlay_handle_events = handle_events;
4162 if (overlay_element) {
4163 gst_video_overlay_handle_events (overlay_element, handle_events);
4164 gst_object_unref (overlay_element);
4169 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4170 gint y, gint width, gint height)
4172 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4173 GstVideoOverlay *overlay_element;
4175 GST_OBJECT_LOCK (playsink);
4176 if (playsink->overlay_element)
4178 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4180 overlay_element = NULL;
4181 GST_OBJECT_UNLOCK (playsink);
4183 playsink->overlay_render_rectangle_set = TRUE;
4184 playsink->overlay_x = x;
4185 playsink->overlay_y = y;
4186 playsink->overlay_width = width;
4187 playsink->overlay_height = height;
4189 if (overlay_element) {
4190 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4192 gst_object_unref (overlay_element);
4197 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4200 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4201 GstVideoOverlay *overlay_element;
4203 GST_OBJECT_LOCK (playsink);
4204 if (playsink->overlay_element)
4206 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4208 overlay_element = NULL;
4209 GST_OBJECT_UNLOCK (playsink);
4211 playsink->overlay_handle_set = TRUE;
4212 playsink->overlay_handle = handle;
4214 if (overlay_element) {
4215 gst_video_overlay_set_window_handle (overlay_element, handle);
4216 gst_object_unref (overlay_element);
4221 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4223 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4224 iface->expose = gst_play_sink_overlay_expose;
4225 iface->handle_events = gst_play_sink_overlay_handle_events;
4226 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4227 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4231 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4232 GstStructure * structure)
4234 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4237 GST_PLAY_SINK_LOCK (playsink);
4238 if (playsink->videochain && playsink->videochain->chain.bin)
4239 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4240 GST_PLAY_SINK_UNLOCK (playsink);
4243 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4246 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4248 gst_object_unref (nav);
4251 gst_object_unref (bin);
4255 gst_structure_free (structure);
4259 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4261 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4263 iface->send_event = gst_play_sink_navigation_send_event;
4266 static const GList *
4267 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4269 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4271 return playsink->colorbalance_channels;
4275 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4276 GstColorBalanceChannel * proxy, gint value)
4278 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4281 GstColorBalance *balance_element = NULL;
4283 GST_OBJECT_LOCK (playsink);
4284 if (playsink->colorbalance_element)
4286 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4287 GST_OBJECT_UNLOCK (playsink);
4289 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4290 GstColorBalanceChannel *proxy_tmp = l->data;
4293 if (proxy_tmp != proxy)
4296 playsink->colorbalance_values[i] = value;
4298 if (balance_element) {
4299 GstColorBalanceChannel *channel = NULL;
4300 const GList *channels, *k;
4302 channels = gst_color_balance_list_channels (balance_element);
4303 for (k = channels; k; k = k->next) {
4304 GstColorBalanceChannel *tmp = l->data;
4306 if (g_strrstr (tmp->label, proxy->label)) {
4314 /* Convert to [0, 1] range */
4317 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4318 (gdouble) proxy->min_value);
4319 /* Convert to channel range */
4321 channel->min_value + new_val * ((gdouble) channel->max_value -
4322 (gdouble) channel->min_value);
4324 gst_color_balance_set_value (balance_element, channel,
4325 (gint) (new_val + 0.5));
4327 gst_object_unref (balance_element);
4330 gst_color_balance_value_changed (balance, proxy, value);
4336 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4337 GstColorBalanceChannel * proxy)
4339 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4343 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4344 GstColorBalanceChannel *proxy_tmp = l->data;
4346 if (proxy_tmp != proxy)
4349 return playsink->colorbalance_values[i];
4352 g_return_val_if_reached (0);
4355 static GstColorBalanceType
4356 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4358 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4359 GstColorBalance *balance_element = NULL;
4360 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4362 GST_OBJECT_LOCK (playsink);
4363 if (playsink->colorbalance_element)
4365 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4366 GST_OBJECT_UNLOCK (playsink);
4368 if (balance_element) {
4369 t = gst_color_balance_get_balance_type (balance_element);
4370 gst_object_unref (balance_element);
4377 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4379 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4381 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4382 iface->set_value = gst_play_sink_colorbalance_set_value;
4383 iface->get_value = gst_play_sink_colorbalance_get_value;
4384 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4388 gst_play_sink_plugin_init (GstPlugin * plugin)
4390 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4391 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4392 GST_TYPE_PLAY_SINK);