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);
313 gst_play_marshal_SAMPLE__BOXED (GClosure * closure,
314 GValue * return_value G_GNUC_UNUSED,
315 guint n_param_values,
316 const GValue * param_values,
317 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
319 typedef GstSample *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
320 gpointer arg_1, gpointer data2);
321 register GMarshalFunc_OBJECT__BOXED callback;
322 register GCClosure *cc = (GCClosure *) closure;
323 register gpointer data1, data2;
325 g_return_if_fail (return_value != NULL);
326 g_return_if_fail (n_param_values == 2);
328 if (G_CCLOSURE_SWAP_DATA (closure)) {
329 data1 = closure->data;
330 data2 = g_value_peek_pointer (param_values + 0);
332 data1 = g_value_peek_pointer (param_values + 0);
333 data2 = closure->data;
336 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
338 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
340 gst_value_take_sample (return_value, v_return);
343 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
345 static void gst_play_sink_overlay_init (gpointer g_iface,
346 gpointer g_iface_data);
347 static void gst_play_sink_navigation_init (gpointer g_iface,
348 gpointer g_iface_data);
349 static void gst_play_sink_colorbalance_init (gpointer g_iface,
350 gpointer g_iface_data);
353 _do_init (GType type)
355 static const GInterfaceInfo svol_info = {
358 static const GInterfaceInfo ov_info = {
359 gst_play_sink_overlay_init,
362 static const GInterfaceInfo nav_info = {
363 gst_play_sink_navigation_init,
366 static const GInterfaceInfo col_info = {
367 gst_play_sink_colorbalance_init,
371 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
372 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
373 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
374 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
377 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
378 _do_init (g_define_type_id));
381 gst_play_sink_class_init (GstPlaySinkClass * klass)
383 GObjectClass *gobject_klass;
384 GstElementClass *gstelement_klass;
385 GstBinClass *gstbin_klass;
387 gobject_klass = (GObjectClass *) klass;
388 gstelement_klass = (GstElementClass *) klass;
389 gstbin_klass = (GstBinClass *) klass;
391 gobject_klass->dispose = gst_play_sink_dispose;
392 gobject_klass->finalize = gst_play_sink_finalize;
393 gobject_klass->set_property = gst_play_sink_set_property;
394 gobject_klass->get_property = gst_play_sink_get_property;
400 * Control the behaviour of playsink.
402 g_object_class_install_property (gobject_klass, PROP_FLAGS,
403 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
404 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
405 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
408 * GstPlaySink:volume:
410 * Get or set the current audio stream volume. 1.0 means 100%,
411 * 0.0 means mute. This uses a linear volume scale.
414 g_object_class_install_property (gobject_klass, PROP_VOLUME,
415 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
416 0.0, VOLUME_MAX_DOUBLE, 1.0,
417 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
418 g_object_class_install_property (gobject_klass, PROP_MUTE,
419 g_param_spec_boolean ("mute", "Mute",
420 "Mute the audio channel without changing the volume", FALSE,
421 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
422 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
423 g_param_spec_string ("subtitle-font-desc",
424 "Subtitle font description",
425 "Pango font description of font "
426 "to be used for subtitle rendering", NULL,
427 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
428 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
429 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
430 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
431 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
432 "be checked for an encoding to use. If that is not set either, "
433 "ISO-8859-15 will be assumed.", NULL,
434 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
435 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
436 g_param_spec_object ("vis-plugin", "Vis plugin",
437 "the visualization element to use (NULL = default)",
438 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
440 * GstPlaySink:sample:
442 * Get the currently rendered or prerolled sample in the video sink.
443 * The #GstCaps in the sample will describe the format of the buffer.
445 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
446 g_param_spec_boxed ("sample", "Sample",
447 "The last sample (NULL = no video available)",
448 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
450 * GstPlaySink:av-offset:
452 * Control the synchronisation offset between the audio and video streams.
453 * Positive values make the audio ahead of the video and negative values make
454 * the audio go behind the video.
458 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
459 g_param_spec_int64 ("av-offset", "AV Offset",
460 "The synchronisation offset between audio and video in nanoseconds",
461 G_MININT64, G_MAXINT64, 0,
462 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
465 * GstPlaySink:video-sink:
467 * Set the used video sink element. NULL will use the default sink. playsink
468 * must be in %GST_STATE_NULL
472 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
473 g_param_spec_object ("video-sink", "Video Sink",
474 "the video output element to use (NULL = default sink)",
475 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
477 * GstPlaySink:audio-sink:
479 * Set the used audio sink element. NULL will use the default sink. playsink
480 * must be in %GST_STATE_NULL
484 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
485 g_param_spec_object ("audio-sink", "Audio Sink",
486 "the audio output element to use (NULL = default sink)",
487 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
490 * GstPlaySink:text-sink:
492 * Set the used text sink element. NULL will use the default sink. playsink
493 * must be in %GST_STATE_NULL
497 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
498 g_param_spec_object ("text-sink", "Text sink",
499 "the text output element to use (NULL = default textoverlay)",
500 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
503 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
504 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
505 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
508 * GstPlaySink::convert-sample
509 * @playsink: a #GstPlaySink
510 * @caps: the target format of the sample
512 * Action signal to retrieve the currently playing video sample in the format
513 * specified by @caps.
514 * If @caps is %NULL, no conversion will be performed and this function is
515 * equivalent to the #GstPlaySink::sample property.
517 * Returns: a #GstSample of the current video sample converted to #caps.
518 * The caps in the sample will describe the final layout of the buffer data.
519 * %NULL is returned when no current sample can be retrieved or when the
522 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
523 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
524 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
525 gst_play_marshal_SAMPLE__BOXED, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
527 gst_element_class_add_pad_template (gstelement_klass,
528 gst_static_pad_template_get (&audiorawtemplate));
529 gst_element_class_add_pad_template (gstelement_klass,
530 gst_static_pad_template_get (&audiotemplate));
531 gst_element_class_add_pad_template (gstelement_klass,
532 gst_static_pad_template_get (&videorawtemplate));
533 gst_element_class_add_pad_template (gstelement_klass,
534 gst_static_pad_template_get (&videotemplate));
535 gst_element_class_add_pad_template (gstelement_klass,
536 gst_static_pad_template_get (&texttemplate));
537 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
539 "Convenience sink for multiple streams",
540 "Wim Taymans <wim.taymans@gmail.com>");
542 gstelement_klass->change_state =
543 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
544 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
545 gstelement_klass->request_new_pad =
546 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
547 gstelement_klass->release_pad =
548 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
550 gstbin_klass->handle_message =
551 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
553 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
554 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
558 gst_play_sink_init (GstPlaySink * playsink)
560 GstColorBalanceChannel *channel;
563 playsink->video_sink = NULL;
564 playsink->audio_sink = NULL;
565 playsink->visualisation = NULL;
566 playsink->text_sink = NULL;
567 playsink->volume = 1.0;
568 playsink->font_desc = NULL;
569 playsink->subtitle_encoding = NULL;
570 playsink->flags = DEFAULT_FLAGS;
572 playsink->stream_synchronizer =
573 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
574 gst_bin_add (GST_BIN_CAST (playsink),
575 GST_ELEMENT_CAST (playsink->stream_synchronizer));
577 g_rec_mutex_init (&playsink->lock);
578 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
581 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
583 channel->label = g_strdup ("CONTRAST");
584 channel->min_value = -1000;
585 channel->max_value = 1000;
586 playsink->colorbalance_channels =
587 g_list_append (playsink->colorbalance_channels, channel);
588 playsink->colorbalance_values[0] = 0;
591 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
593 channel->label = g_strdup ("BRIGHTNESS");
594 channel->min_value = -1000;
595 channel->max_value = 1000;
596 playsink->colorbalance_channels =
597 g_list_append (playsink->colorbalance_channels, channel);
598 playsink->colorbalance_values[1] = 0;
601 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
603 channel->label = g_strdup ("HUE");
604 channel->min_value = -1000;
605 channel->max_value = 1000;
606 playsink->colorbalance_channels =
607 g_list_append (playsink->colorbalance_channels, channel);
608 playsink->colorbalance_values[2] = 0;
611 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
613 channel->label = g_strdup ("SATURATION");
614 channel->min_value = -1000;
615 channel->max_value = 1000;
616 playsink->colorbalance_channels =
617 g_list_append (playsink->colorbalance_channels, channel);
618 playsink->colorbalance_values[3] = 0;
622 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
626 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
629 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
635 free_chain (GstPlayChain * chain)
639 gst_object_unref (chain->bin);
645 gst_play_sink_dispose (GObject * object)
647 GstPlaySink *playsink;
649 playsink = GST_PLAY_SINK (object);
651 if (playsink->audio_sink != NULL) {
652 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
653 gst_object_unref (playsink->audio_sink);
654 playsink->audio_sink = NULL;
656 if (playsink->video_sink != NULL) {
657 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
658 gst_object_unref (playsink->video_sink);
659 playsink->video_sink = NULL;
661 if (playsink->visualisation != NULL) {
662 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
663 gst_object_unref (playsink->visualisation);
664 playsink->visualisation = NULL;
666 if (playsink->text_sink != NULL) {
667 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
668 gst_object_unref (playsink->text_sink);
669 playsink->text_sink = NULL;
672 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
673 playsink->videodeinterlacechain = NULL;
674 free_chain ((GstPlayChain *) playsink->videochain);
675 playsink->videochain = NULL;
676 free_chain ((GstPlayChain *) playsink->audiochain);
677 playsink->audiochain = NULL;
678 free_chain ((GstPlayChain *) playsink->vischain);
679 playsink->vischain = NULL;
680 free_chain ((GstPlayChain *) playsink->textchain);
681 playsink->textchain = NULL;
683 if (playsink->audio_tee_sink) {
684 gst_object_unref (playsink->audio_tee_sink);
685 playsink->audio_tee_sink = NULL;
688 if (playsink->audio_tee_vissrc) {
689 gst_element_release_request_pad (playsink->audio_tee,
690 playsink->audio_tee_vissrc);
691 gst_object_unref (playsink->audio_tee_vissrc);
692 playsink->audio_tee_vissrc = NULL;
695 if (playsink->audio_tee_asrc) {
696 gst_element_release_request_pad (playsink->audio_tee,
697 playsink->audio_tee_asrc);
698 gst_object_unref (playsink->audio_tee_asrc);
699 playsink->audio_tee_asrc = NULL;
702 g_free (playsink->font_desc);
703 playsink->font_desc = NULL;
705 g_free (playsink->subtitle_encoding);
706 playsink->subtitle_encoding = NULL;
708 playsink->stream_synchronizer = NULL;
710 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
712 g_list_free (playsink->colorbalance_channels);
713 playsink->colorbalance_channels = NULL;
715 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
719 gst_play_sink_finalize (GObject * object)
721 GstPlaySink *playsink;
723 playsink = GST_PLAY_SINK (object);
725 g_rec_mutex_clear (&playsink->lock);
727 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
731 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
734 GstElement **elem = NULL, *old = NULL;
736 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
738 GST_PLAY_SINK_LOCK (playsink);
740 case GST_PLAY_SINK_TYPE_AUDIO:
741 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
742 elem = &playsink->audio_sink;
744 case GST_PLAY_SINK_TYPE_VIDEO:
745 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
746 elem = &playsink->video_sink;
748 case GST_PLAY_SINK_TYPE_TEXT:
749 elem = &playsink->text_sink;
757 gst_object_ref (sink);
760 GST_PLAY_SINK_UNLOCK (playsink);
764 gst_element_set_state (old, GST_STATE_NULL);
765 gst_object_unref (old);
770 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
772 GstElement *result = NULL;
773 GstElement *elem = NULL, *chainp = NULL;
775 GST_PLAY_SINK_LOCK (playsink);
777 case GST_PLAY_SINK_TYPE_AUDIO:
778 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
780 GstPlayAudioChain *chain;
781 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
782 chainp = chain->sink;
783 elem = playsink->audio_sink;
786 case GST_PLAY_SINK_TYPE_VIDEO:
787 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
789 GstPlayVideoChain *chain;
790 if ((chain = (GstPlayVideoChain *) playsink->videochain))
791 chainp = chain->sink;
792 elem = playsink->video_sink;
795 case GST_PLAY_SINK_TYPE_TEXT:
797 GstPlayTextChain *chain;
798 if ((chain = (GstPlayTextChain *) playsink->textchain))
799 chainp = chain->sink;
800 elem = playsink->text_sink;
807 /* we have an active chain with a sink, get the sink */
808 result = gst_object_ref (chainp);
810 /* nothing found, return last configured sink */
811 if (result == NULL && elem)
812 result = gst_object_ref (elem);
813 GST_PLAY_SINK_UNLOCK (playsink);
818 static GstPadProbeReturn
819 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
822 GstPlaySink *playsink;
823 GstPlayVisChain *chain;
825 playsink = GST_PLAY_SINK (user_data);
827 GST_PLAY_SINK_LOCK (playsink);
828 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
829 /* now try to change the plugin in the running vis chain */
830 if (!(chain = (GstPlayVisChain *) playsink->vischain))
833 /* unlink the old plugin and unghost the pad */
834 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
835 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
837 /* set the old plugin to NULL and remove */
838 gst_element_set_state (chain->vis, GST_STATE_NULL);
839 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
841 /* add new plugin and set state to playing */
842 chain->vis = playsink->visualisation;
843 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
844 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
847 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
848 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
851 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
852 GST_PAD_LINK_CHECK_NOTHING);
853 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
857 GST_PLAY_SINK_UNLOCK (playsink);
859 /* remove the probe and unblock the pad */
860 return GST_PAD_PROBE_REMOVE;
864 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
866 GstPlayVisChain *chain;
868 /* setting NULL means creating the default vis plugin */
870 vis = gst_element_factory_make ("goom", "vis");
872 /* simply return if we don't have a vis plugin here */
876 GST_PLAY_SINK_LOCK (playsink);
877 /* first store the new vis */
878 if (playsink->visualisation)
879 gst_object_unref (playsink->visualisation);
881 gst_object_ref_sink (vis);
882 playsink->visualisation = vis;
884 /* now try to change the plugin in the running vis chain, if we have no chain,
885 * we don't bother, any future vis chain will be created with the new vis
887 if (!(chain = (GstPlayVisChain *) playsink->vischain))
890 /* block the pad, the next time the callback is called we can change the
891 * visualisation. It's possible that this never happens or that the pad was
892 * already blocked. If the callback never happens, we don't have new data so
893 * we don't need the new vis plugin. If the pad was already blocked, the
894 * function returns FALSE but the previous pad block will do the right thing
896 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
897 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
898 gst_play_sink_vis_blocked, playsink, NULL);
900 GST_PLAY_SINK_UNLOCK (playsink);
906 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
908 GstElement *result = NULL;
909 GstPlayVisChain *chain;
911 GST_PLAY_SINK_LOCK (playsink);
912 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
913 /* we have an active chain, get the sink */
915 result = gst_object_ref (chain->vis);
917 /* nothing found, return last configured sink */
918 if (result == NULL && playsink->visualisation)
919 result = gst_object_ref (playsink->visualisation);
920 GST_PLAY_SINK_UNLOCK (playsink);
926 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
928 GstPlayAudioChain *chain;
930 GST_PLAY_SINK_LOCK (playsink);
931 playsink->volume = volume;
932 chain = (GstPlayAudioChain *) playsink->audiochain;
933 if (chain && chain->volume) {
934 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
935 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
936 chain->mute, volume, playsink->mute);
937 /* if there is a mute element or we are not muted, set the volume */
938 if (chain->mute || !playsink->mute)
939 g_object_set (chain->volume, "volume", volume, NULL);
941 GST_LOG_OBJECT (playsink, "no volume element");
942 playsink->volume_changed = TRUE;
944 GST_PLAY_SINK_UNLOCK (playsink);
948 gst_play_sink_get_volume (GstPlaySink * playsink)
951 GstPlayAudioChain *chain;
953 GST_PLAY_SINK_LOCK (playsink);
954 chain = (GstPlayAudioChain *) playsink->audiochain;
955 result = playsink->volume;
956 if (chain && chain->volume) {
957 if (chain->mute || !playsink->mute) {
958 g_object_get (chain->volume, "volume", &result, NULL);
959 playsink->volume = result;
962 GST_PLAY_SINK_UNLOCK (playsink);
968 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
970 GstPlayAudioChain *chain;
972 GST_PLAY_SINK_LOCK (playsink);
973 playsink->mute = mute;
974 chain = (GstPlayAudioChain *) playsink->audiochain;
977 g_object_set (chain->mute, "mute", mute, NULL);
978 } else if (chain->volume) {
980 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
982 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
986 playsink->mute_changed = TRUE;
988 GST_PLAY_SINK_UNLOCK (playsink);
992 gst_play_sink_get_mute (GstPlaySink * playsink)
995 GstPlayAudioChain *chain;
997 GST_PLAY_SINK_LOCK (playsink);
998 chain = (GstPlayAudioChain *) playsink->audiochain;
999 if (chain && chain->mute) {
1000 g_object_get (chain->mute, "mute", &result, NULL);
1001 playsink->mute = result;
1003 result = playsink->mute;
1005 GST_PLAY_SINK_UNLOCK (playsink);
1011 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1015 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1016 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1020 add_chain (GstPlayChain * chain, gboolean add)
1022 if (chain->added == add)
1026 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1028 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1029 /* we don't want to lose our sink status */
1030 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1039 activate_chain (GstPlayChain * chain, gboolean activate)
1043 if (chain->activated == activate)
1046 GST_OBJECT_LOCK (chain->playsink);
1047 state = GST_STATE_TARGET (chain->playsink);
1048 GST_OBJECT_UNLOCK (chain->playsink);
1051 gst_element_set_state (chain->bin, state);
1053 gst_element_set_state (chain->bin, GST_STATE_NULL);
1055 chain->activated = activate;
1061 element_is_sink (GstElement * element)
1065 GST_OBJECT_LOCK (element);
1066 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1067 GST_OBJECT_UNLOCK (element);
1069 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1074 element_has_property (GstElement * element, const gchar * pname, GType type)
1078 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1080 if (pspec == NULL) {
1081 GST_DEBUG_OBJECT (element, "no %s property", pname);
1085 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1086 g_type_is_a (pspec->value_type, type)) {
1087 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1088 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1092 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1093 "and we expected it to be of type %s", pname,
1094 g_type_name (pspec->value_type), g_type_name (type));
1101 const gchar *prop_name;
1104 } FindPropertyHelper;
1107 find_property (const GValue * item, FindPropertyHelper * helper)
1109 GstElement *element = g_value_get_object (item);
1110 if (helper->need_sink && !element_is_sink (element)) {
1114 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1118 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1119 (helper->need_sink) ? "sink" : "element");
1120 return 0; /* keep it */
1123 /* FIXME: why not move these functions into core? */
1124 /* find a sink in the hierarchy with a property named @name. This function does
1125 * not increase the refcount of the returned object and thus remains valid as
1126 * long as the bin is valid. */
1128 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1129 const gchar * name, GType expected_type)
1131 GstElement *result = NULL;
1134 if (element_has_property (obj, name, expected_type)) {
1136 } else if (GST_IS_BIN (obj)) {
1138 GValue item = { 0, };
1139 FindPropertyHelper helper = { name, expected_type, TRUE };
1141 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1142 found = gst_iterator_find_custom (it,
1143 (GCompareFunc) find_property, &item, &helper);
1144 gst_iterator_free (it);
1146 result = g_value_get_object (&item);
1147 /* we don't need the extra ref */
1148 g_value_unset (&item);
1154 /* find an object in the hierarchy with a property named @name */
1156 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1157 const gchar * name, GType expected_type)
1159 GstElement *result = NULL;
1162 if (GST_IS_BIN (obj)) {
1164 GValue item = { 0, };
1165 FindPropertyHelper helper = { name, expected_type, FALSE };
1167 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1168 found = gst_iterator_find_custom (it,
1169 (GCompareFunc) find_property, &item, &helper);
1170 gst_iterator_free (it);
1172 result = g_value_dup_object (&item);
1173 g_value_unset (&item);
1175 if (element_has_property (obj, name, expected_type)) {
1177 gst_object_ref (obj);
1184 do_async_start (GstPlaySink * playsink)
1186 GstMessage *message;
1188 if (!playsink->need_async_start) {
1189 GST_INFO_OBJECT (playsink, "no async_start needed");
1193 playsink->async_pending = TRUE;
1195 GST_INFO_OBJECT (playsink, "Sending async_start message");
1196 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1197 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1198 (playsink), message);
1202 do_async_done (GstPlaySink * playsink)
1204 GstMessage *message;
1206 if (playsink->async_pending) {
1207 GST_INFO_OBJECT (playsink, "Sending async_done message");
1208 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1209 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1210 (playsink), message);
1212 playsink->async_pending = FALSE;
1215 playsink->need_async_start = FALSE;
1218 /* try to change the state of an element. This function returns the element when
1219 * the state change could be performed. When this function returns NULL an error
1220 * occured and the element is unreffed if @unref is TRUE. */
1222 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1224 GstStateChangeReturn ret;
1227 ret = gst_element_set_state (element, GST_STATE_READY);
1228 if (ret == GST_STATE_CHANGE_FAILURE) {
1229 GST_DEBUG_OBJECT (playsink, "failed state change..");
1230 gst_element_set_state (element, GST_STATE_NULL);
1232 gst_object_unref (element);
1239 /* make the element (bin) that contains the elements needed to perform
1240 * video display. Only used for *raw* video streams.
1242 * +------------------------------------------------------------+
1244 * | +-------+ +----------+ +----------+ +---------+ |
1245 * | | queue | |colorspace| |videoscale| |videosink| |
1246 * | +-sink src-sink src-sink src-sink | |
1247 * | | +-------+ +----------+ +----------+ +---------+ |
1249 * +------------------------------------------------------------+
1252 static GstPlayVideoDeinterlaceChain *
1253 gen_video_deinterlace_chain (GstPlaySink * playsink)
1255 GstPlayVideoDeinterlaceChain *chain;
1258 GstElement *head = NULL, *prev = NULL;
1260 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1261 chain->chain.playsink = playsink;
1263 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1265 /* create a bin to hold objects, as we create them we add them to this bin so
1266 * that when something goes wrong we only need to unref the bin */
1267 chain->chain.bin = gst_bin_new ("vdbin");
1268 bin = GST_BIN_CAST (chain->chain.bin);
1269 gst_object_ref_sink (bin);
1271 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1272 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1273 if (chain->conv == NULL) {
1274 post_missing_element_message (playsink, COLORSPACE);
1275 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1276 (_("Missing element '%s' - check your GStreamer installation."),
1277 COLORSPACE), ("video rendering might fail"));
1279 gst_bin_add (bin, chain->conv);
1284 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1285 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1286 if (chain->deinterlace == NULL) {
1287 post_missing_element_message (playsink, "deinterlace");
1288 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1289 (_("Missing element '%s' - check your GStreamer installation."),
1290 "deinterlace"), ("deinterlacing won't work"));
1292 gst_bin_add (bin, chain->deinterlace);
1294 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1295 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1298 head = chain->deinterlace;
1300 prev = chain->deinterlace;
1304 pad = gst_element_get_static_pad (head, "sink");
1305 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1306 gst_object_unref (pad);
1308 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1312 pad = gst_element_get_static_pad (prev, "src");
1313 chain->srcpad = gst_ghost_pad_new ("src", pad);
1314 gst_object_unref (pad);
1316 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1319 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1320 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1326 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1327 (NULL), ("Failed to configure the video deinterlace chain."));
1328 free_chain ((GstPlayChain *) chain);
1334 is_valid_color_balance_element (GstColorBalance * bal)
1336 gboolean have_brightness = FALSE;
1337 gboolean have_contrast = FALSE;
1338 gboolean have_hue = FALSE;
1339 gboolean have_saturation = FALSE;
1340 const GList *channels, *l;
1342 channels = gst_color_balance_list_channels (bal);
1343 for (l = channels; l; l = l->next) {
1344 GstColorBalanceChannel *ch = l->data;
1346 if (g_strrstr (ch->label, "BRIGHTNESS"))
1347 have_brightness = TRUE;
1348 else if (g_strrstr (ch->label, "CONTRAST"))
1349 have_contrast = TRUE;
1350 else if (g_strrstr (ch->label, "HUE"))
1352 else if (g_strrstr (ch->label, "SATURATION"))
1353 have_saturation = TRUE;
1356 return have_brightness && have_contrast && have_hue && have_saturation;
1360 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1363 GstColorBalance *cb, **cb_out = user_data;
1365 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1366 valid = is_valid_color_balance_element (cb);
1369 && gst_color_balance_get_balance_type (*cb_out) ==
1370 GST_COLOR_BALANCE_SOFTWARE) {
1371 gst_object_unref (*cb_out);
1372 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1373 } else if (!*cb_out) {
1374 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1379 static GstColorBalance *
1380 find_color_balance_element (GstElement * element)
1383 GstColorBalance *cb = NULL;
1385 if (GST_IS_COLOR_BALANCE (element)
1386 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1387 return GST_COLOR_BALANCE (gst_object_ref (element));
1388 else if (!GST_IS_BIN (element))
1391 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1392 GST_TYPE_COLOR_BALANCE);
1393 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1394 &cb) == GST_ITERATOR_RESYNC)
1395 gst_iterator_resync (it);
1396 gst_iterator_free (it);
1402 colorbalance_value_changed_cb (GstColorBalance * balance,
1403 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1408 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1409 GstColorBalanceChannel *proxy = l->data;
1411 if (g_strrstr (channel->label, proxy->label)) {
1414 /* Convert to [0, 1] range */
1417 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1418 (gdouble) channel->min_value);
1419 /* Convert to proxy range */
1421 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1422 (gdouble) proxy->min_value);
1423 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1425 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1426 playsink->colorbalance_values[i]);
1433 update_colorbalance (GstPlaySink * playsink)
1435 GstColorBalance *balance = NULL;
1439 GST_OBJECT_LOCK (playsink);
1440 if (playsink->colorbalance_element) {
1442 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1444 GST_OBJECT_UNLOCK (playsink);
1448 g_signal_handlers_block_by_func (balance,
1449 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1451 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1452 GstColorBalanceChannel *proxy = l->data;
1453 GstColorBalanceChannel *channel = NULL;
1454 const GList *channels, *k;
1456 channels = gst_color_balance_list_channels (balance);
1457 for (k = channels; k; k = k->next) {
1458 GstColorBalanceChannel *tmp = k->data;
1460 if (g_strrstr (tmp->label, proxy->label)) {
1468 gst_color_balance_set_value (balance, channel,
1469 playsink->colorbalance_values[i]);
1472 g_signal_handlers_unblock_by_func (balance,
1473 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1476 /* make the element (bin) that contains the elements needed to perform
1479 * +------------------------------------------------------------+
1481 * | +-------+ +----------+ +----------+ +---------+ |
1482 * | | queue | |colorspace| |videoscale| |videosink| |
1483 * | +-sink src-sink src-sink src-sink | |
1484 * | | +-------+ +----------+ +----------+ +---------+ |
1486 * +------------------------------------------------------------+
1489 static GstPlayVideoChain *
1490 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1492 GstPlayVideoChain *chain;
1495 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1497 chain = g_new0 (GstPlayVideoChain, 1);
1498 chain->chain.playsink = playsink;
1499 chain->chain.raw = raw;
1501 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1503 if (playsink->video_sink) {
1504 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1505 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1507 /* only try fallback if no specific sink was chosen */
1508 if (chain->sink == NULL) {
1509 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1510 elem = gst_element_factory_make ("autovideosink", "videosink");
1511 chain->sink = try_element (playsink, elem, TRUE);
1513 if (chain->sink == NULL) {
1514 /* if default sink from config.h is different then try it too */
1515 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1516 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1517 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1518 chain->sink = try_element (playsink, elem, TRUE);
1522 playsink->video_sink = gst_object_ref (chain->sink);
1524 if (chain->sink == NULL)
1528 /* if we can disable async behaviour of the sink, we can avoid adding a
1529 * queue for the audio chain. */
1531 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1534 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1535 async, GST_ELEMENT_NAME (elem));
1536 g_object_set (elem, "async", async, NULL);
1537 chain->async = async;
1539 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1540 chain->async = TRUE;
1543 /* Make sure the aspect ratio is kept */
1545 gst_play_sink_find_property_sinks (playsink, chain->sink,
1546 "force-aspect-ratio", G_TYPE_BOOLEAN);
1548 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1550 /* find ts-offset element */
1551 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1552 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1555 /* create a bin to hold objects, as we create them we add them to this bin so
1556 * that when something goes wrong we only need to unref the bin */
1557 chain->chain.bin = gst_bin_new ("vbin");
1558 bin = GST_BIN_CAST (chain->chain.bin);
1559 gst_object_ref_sink (bin);
1560 gst_bin_add (bin, chain->sink);
1562 /* Get the VideoOverlay element */
1564 GstVideoOverlay *overlay = NULL;
1566 GST_OBJECT_LOCK (playsink);
1567 if (playsink->overlay_element)
1568 gst_object_unref (playsink->overlay_element);
1569 playsink->overlay_element =
1570 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1571 GST_TYPE_VIDEO_OVERLAY));
1572 if (playsink->overlay_element)
1573 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1574 GST_OBJECT_UNLOCK (playsink);
1577 if (playsink->overlay_handle_set)
1578 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1579 if (playsink->overlay_handle_events_set)
1580 gst_video_overlay_handle_events (overlay,
1581 playsink->overlay_handle_events);
1582 if (playsink->overlay_render_rectangle_set)
1583 gst_video_overlay_set_render_rectangle (overlay,
1584 playsink->overlay_x, playsink->overlay_y,
1585 playsink->overlay_width, playsink->overlay_height);
1586 gst_object_unref (overlay);
1590 /* decouple decoder from sink, this improves playback quite a lot since the
1591 * decoder can continue while the sink blocks for synchronisation. We don't
1592 * need a lot of buffers as this consumes a lot of memory and we don't want
1593 * too little because else we would be context switching too quickly. */
1594 chain->queue = gst_element_factory_make ("queue", "vqueue");
1595 if (chain->queue == NULL) {
1596 post_missing_element_message (playsink, "queue");
1597 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1598 (_("Missing element '%s' - check your GStreamer installation."),
1599 "queue"), ("video rendering might be suboptimal"));
1603 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1604 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1605 gst_bin_add (bin, chain->queue);
1606 head = prev = chain->queue;
1609 GST_OBJECT_LOCK (playsink);
1610 if (playsink->colorbalance_element) {
1611 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1612 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1613 gst_object_unref (playsink->colorbalance_element);
1615 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1616 GST_OBJECT_UNLOCK (playsink);
1618 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1619 || (!playsink->colorbalance_element
1620 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1621 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1622 gboolean use_balance = !playsink->colorbalance_element
1623 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1625 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1627 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1628 "use-converters", use_converters, "use-balance", use_balance, NULL);
1630 GST_OBJECT_LOCK (playsink);
1631 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1632 playsink->colorbalance_element =
1633 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1634 (chain->conv)->balance));
1635 GST_OBJECT_UNLOCK (playsink);
1637 gst_bin_add (bin, chain->conv);
1639 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1640 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1648 update_colorbalance (playsink);
1651 GST_DEBUG_OBJECT (playsink, "linking to sink");
1652 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1653 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1657 pad = gst_element_get_static_pad (head, "sink");
1658 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1659 gst_object_unref (pad);
1661 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1668 if (!elem && !playsink->video_sink) {
1669 post_missing_element_message (playsink, "autovideosink");
1670 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1671 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1672 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1673 (_("Both autovideosink and %s elements are missing."),
1674 DEFAULT_VIDEOSINK), (NULL));
1676 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1677 (_("The autovideosink element is missing.")), (NULL));
1680 if (playsink->video_sink) {
1681 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1682 (_("Configured videosink %s is not working."),
1683 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1684 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1685 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1686 (_("Both autovideosink and %s elements are not working."),
1687 DEFAULT_VIDEOSINK), (NULL));
1689 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1690 (_("The autovideosink element is not working.")), (NULL));
1693 free_chain ((GstPlayChain *) chain);
1699 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1700 (NULL), ("Failed to configure the video sink."));
1701 /* checking sink made it READY */
1702 gst_element_set_state (chain->sink, GST_STATE_NULL);
1703 /* Remove chain from the bin to allow reuse later */
1704 gst_bin_remove (bin, chain->sink);
1705 free_chain ((GstPlayChain *) chain);
1711 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1714 GstPlayVideoChain *chain;
1715 GstStateChangeReturn ret;
1717 chain = playsink->videochain;
1719 chain->chain.raw = raw;
1721 /* if the chain was active we don't do anything */
1722 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1725 /* try to set the sink element to READY again */
1726 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1727 if (ret == GST_STATE_CHANGE_FAILURE)
1730 /* Get the VideoOverlay element */
1732 GstVideoOverlay *overlay = NULL;
1734 GST_OBJECT_LOCK (playsink);
1735 if (playsink->overlay_element)
1736 gst_object_unref (playsink->overlay_element);
1737 playsink->overlay_element =
1738 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1739 GST_TYPE_VIDEO_OVERLAY));
1740 if (playsink->overlay_element)
1741 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1742 GST_OBJECT_UNLOCK (playsink);
1745 if (playsink->overlay_handle_set)
1746 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1747 if (playsink->overlay_handle_events_set)
1748 gst_video_overlay_handle_events (overlay,
1749 playsink->overlay_handle_events);
1750 if (playsink->overlay_render_rectangle_set)
1751 gst_video_overlay_set_render_rectangle (overlay,
1752 playsink->overlay_x, playsink->overlay_y,
1753 playsink->overlay_width, playsink->overlay_height);
1754 gst_object_unref (overlay);
1758 /* find ts-offset element */
1759 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1760 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1763 /* if we can disable async behaviour of the sink, we can avoid adding a
1764 * queue for the audio chain. */
1766 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1769 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1770 async, GST_ELEMENT_NAME (elem));
1771 g_object_set (elem, "async", async, NULL);
1772 chain->async = async;
1774 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1775 chain->async = TRUE;
1778 /* Make sure the aspect ratio is kept */
1780 gst_play_sink_find_property_sinks (playsink, chain->sink,
1781 "force-aspect-ratio", G_TYPE_BOOLEAN);
1783 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1785 GST_OBJECT_LOCK (playsink);
1786 if (playsink->colorbalance_element) {
1787 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1788 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1789 gst_object_unref (playsink->colorbalance_element);
1791 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1792 GST_OBJECT_UNLOCK (playsink);
1795 gboolean use_balance = !playsink->colorbalance_element
1796 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1798 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1800 GST_OBJECT_LOCK (playsink);
1801 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1802 playsink->colorbalance_element =
1803 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1804 (chain->conv)->balance));
1805 GST_OBJECT_UNLOCK (playsink);
1808 update_colorbalance (playsink);
1813 /* make an element for playback of video with subtitles embedded.
1814 * Only used for *raw* video streams.
1816 * +--------------------------------------------+
1818 * | +--------+ +-----------------+ |
1819 * | | queue | | subtitleoverlay | |
1820 * video--src sink---video_sink | |
1821 * | +--------+ | src--src
1822 * text------------------text_sink | |
1823 * | +-----------------+ |
1824 * +--------------------------------------------+
1827 static GstPlayTextChain *
1828 gen_text_chain (GstPlaySink * playsink)
1830 GstPlayTextChain *chain;
1833 GstPad *videosinkpad, *textsinkpad, *srcpad;
1835 chain = g_new0 (GstPlayTextChain, 1);
1836 chain->chain.playsink = playsink;
1838 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1840 chain->chain.bin = gst_bin_new ("tbin");
1841 bin = GST_BIN_CAST (chain->chain.bin);
1842 gst_object_ref_sink (bin);
1844 videosinkpad = textsinkpad = srcpad = NULL;
1846 /* first try to hook the text pad to the custom sink */
1847 if (playsink->text_sink) {
1848 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1849 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1852 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1855 /* make sure the sparse subtitles don't participate in the preroll */
1856 g_object_set (elem, "async", FALSE, NULL);
1857 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1858 gst_bin_add (bin, chain->sink);
1859 /* NOTE streamsynchronizer needs streams decoupled */
1860 /* make a little queue */
1861 chain->queue = gst_element_factory_make ("queue", "subqueue");
1862 if (chain->queue == NULL) {
1863 post_missing_element_message (playsink, "queue");
1864 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1865 (_("Missing element '%s' - check your GStreamer installation."),
1866 "queue"), ("rendering might be suboptimal"));
1868 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1869 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1870 "silent", TRUE, NULL);
1871 gst_bin_add (bin, chain->queue);
1873 /* we have a custom sink, this will be our textsinkpad */
1874 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1875 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1876 /* we're all fine now and we can add the sink to the chain */
1877 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1878 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1880 GST_WARNING_OBJECT (playsink,
1881 "can't find a sink pad on custom text sink");
1882 gst_bin_remove (bin, chain->sink);
1883 gst_bin_remove (bin, chain->queue);
1885 chain->queue = NULL;
1887 /* try to set sync to true but it's no biggie when we can't */
1888 if (chain->sink && (elem =
1889 gst_play_sink_find_property_sinks (playsink, chain->sink,
1890 "sync", G_TYPE_BOOLEAN)))
1891 g_object_set (elem, "sync", TRUE, NULL);
1894 gst_bin_remove (bin, chain->sink);
1896 GST_WARNING_OBJECT (playsink,
1897 "can't find async property in custom text sink");
1900 if (textsinkpad == NULL) {
1901 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1902 (_("Custom text sink element is not usable.")),
1903 ("fallback to default textoverlay"));
1907 if (textsinkpad == NULL) {
1908 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1909 /* make a little queue */
1910 chain->queue = gst_element_factory_make ("queue", "vqueue");
1911 if (chain->queue == NULL) {
1912 post_missing_element_message (playsink, "queue");
1913 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1914 (_("Missing element '%s' - check your GStreamer installation."),
1915 "queue"), ("video rendering might be suboptimal"));
1917 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1918 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1919 "silent", TRUE, NULL);
1920 gst_bin_add (bin, chain->queue);
1921 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1925 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1926 if (chain->overlay == NULL) {
1927 post_missing_element_message (playsink, "subtitleoverlay");
1928 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1929 (_("Missing element '%s' - check your GStreamer installation."),
1930 "subtitleoverlay"), ("subtitle rendering disabled"));
1932 GstElement *element;
1934 gst_bin_add (bin, chain->overlay);
1936 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1937 if (playsink->font_desc) {
1938 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1939 playsink->font_desc, NULL);
1941 if (playsink->subtitle_encoding) {
1942 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1943 playsink->subtitle_encoding, NULL);
1946 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1947 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1949 /* make another little queue to decouple streams */
1950 element = gst_element_factory_make ("queue", "subqueue");
1951 if (element == NULL) {
1952 post_missing_element_message (playsink, "queue");
1953 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1954 (_("Missing element '%s' - check your GStreamer installation."),
1955 "queue"), ("rendering might be suboptimal"));
1957 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1958 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1959 "silent", TRUE, NULL);
1960 gst_bin_add (bin, element);
1961 if (gst_element_link_pads_full (element, "src", chain->overlay,
1962 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1963 textsinkpad = gst_element_get_static_pad (element, "sink");
1964 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1966 gst_bin_remove (bin, chain->sink);
1967 gst_bin_remove (bin, chain->overlay);
1969 chain->overlay = NULL;
1970 gst_object_unref (videosinkpad);
1971 videosinkpad = NULL;
1978 if (videosinkpad == NULL) {
1979 /* if we still don't have a videosink, we don't have an overlay. the only
1980 * thing we can do is insert an identity and ghost the src
1982 chain->identity = gst_element_factory_make ("identity", "tidentity");
1983 if (chain->identity == NULL) {
1984 post_missing_element_message (playsink, "identity");
1985 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1986 (_("Missing element '%s' - check your GStreamer installation."),
1987 "identity"), (NULL));
1989 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1990 g_object_set (chain->identity, "silent", TRUE, NULL);
1991 gst_bin_add (bin, chain->identity);
1992 srcpad = gst_element_get_static_pad (chain->identity, "src");
1993 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1997 /* expose the ghostpads */
1999 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2000 gst_object_unref (videosinkpad);
2001 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2004 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2005 gst_object_unref (textsinkpad);
2006 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2009 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2010 gst_object_unref (srcpad);
2011 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2018 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2022 g_object_get (object, "volume", &vol, NULL);
2023 playsink->volume = vol;
2025 g_object_notify (G_OBJECT (playsink), "volume");
2029 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2033 g_object_get (object, "mute", &mute, NULL);
2034 playsink->mute = mute;
2036 g_object_notify (G_OBJECT (playsink), "mute");
2039 /* make the chain that contains the elements needed to perform
2042 * We add a tee as the first element so that we can link the visualisation chain
2043 * to it when requested.
2045 * +-------------------------------------------------------------+
2047 * | +---------+ +----------+ +---------+ +---------+ |
2048 * | |audioconv| |audioscale| | volume | |audiosink| |
2049 * | +-srck src-sink src-sink src-sink | |
2050 * | | +---------+ +----------+ +---------+ +---------+ |
2052 * +-------------------------------------------------------------+
2054 static GstPlayAudioChain *
2055 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2057 GstPlayAudioChain *chain;
2059 gboolean have_volume;
2061 GstElement *head, *prev, *elem = NULL;
2063 chain = g_new0 (GstPlayAudioChain, 1);
2064 chain->chain.playsink = playsink;
2065 chain->chain.raw = raw;
2067 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2069 if (playsink->audio_sink) {
2070 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2071 playsink->audio_sink);
2072 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2074 /* only try fallback if no specific sink was chosen */
2075 if (chain->sink == NULL) {
2076 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2077 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2078 chain->sink = try_element (playsink, elem, TRUE);
2080 if (chain->sink == NULL) {
2081 /* if default sink from config.h is different then try it too */
2082 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2083 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2084 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2085 chain->sink = try_element (playsink, elem, TRUE);
2089 playsink->audio_sink = gst_object_ref (chain->sink);
2091 if (chain->sink == NULL)
2094 chain->chain.bin = gst_bin_new ("abin");
2095 bin = GST_BIN_CAST (chain->chain.bin);
2096 gst_object_ref_sink (bin);
2097 gst_bin_add (bin, chain->sink);
2099 /* we have to add a queue when we need to decouple for the video sink in
2100 * visualisations and for streamsynchronizer */
2101 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2102 chain->queue = gst_element_factory_make ("queue", "aqueue");
2103 if (chain->queue == NULL) {
2104 post_missing_element_message (playsink, "queue");
2105 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2106 (_("Missing element '%s' - check your GStreamer installation."),
2107 "queue"), ("audio playback and visualizations might not work"));
2111 g_object_set (chain->queue, "silent", TRUE, NULL);
2112 gst_bin_add (bin, chain->queue);
2113 prev = head = chain->queue;
2116 /* find ts-offset element */
2117 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2118 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2121 /* check if the sink, or something within the sink, has the volume property.
2122 * If it does we don't need to add a volume element. */
2124 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2127 chain->volume = elem;
2129 g_signal_connect (chain->volume, "notify::volume",
2130 G_CALLBACK (notify_volume_cb), playsink);
2132 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2134 chain->sink_volume = TRUE;
2135 /* if the sink also has a mute property we can use this as well. We'll only
2136 * use the mute property if there is a volume property. We can simulate the
2137 * mute with the volume otherwise. */
2139 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2142 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2143 g_signal_connect (chain->mute, "notify::mute",
2144 G_CALLBACK (notify_mute_cb), playsink);
2146 /* use the sink to control the volume and mute */
2147 if (playsink->volume_changed) {
2148 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2149 playsink->volume_changed = FALSE;
2151 if (playsink->mute_changed) {
2153 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2156 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2158 playsink->mute_changed = FALSE;
2161 /* no volume, we need to add a volume element when we can */
2162 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2163 have_volume = FALSE;
2164 chain->sink_volume = FALSE;
2167 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2168 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2169 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2170 gboolean use_volume =
2171 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2172 GST_DEBUG_OBJECT (playsink,
2173 "creating audioconvert with use-converters %d, use-volume %d",
2174 use_converters, use_volume);
2176 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2177 "use-converters", use_converters, "use-volume", use_volume, NULL);
2178 gst_bin_add (bin, chain->conv);
2180 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2181 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2188 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2189 GstPlaySinkAudioConvert *conv =
2190 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2193 chain->volume = conv->volume;
2196 g_signal_connect (chain->volume, "notify::volume",
2197 G_CALLBACK (notify_volume_cb), playsink);
2199 /* volume also has the mute property */
2200 chain->mute = chain->volume;
2201 g_signal_connect (chain->mute, "notify::mute",
2202 G_CALLBACK (notify_mute_cb), playsink);
2204 /* configure with the latest volume and mute */
2205 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2207 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2213 /* we only have to link to the previous element if we have something in
2214 * front of the sink */
2215 GST_DEBUG_OBJECT (playsink, "linking to sink");
2216 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2217 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2221 /* post a warning if we have no way to configure the volume */
2223 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2224 (_("No volume control found")), ("Volume/mute is not available"));
2227 /* and ghost the sinkpad of the headmost element */
2228 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2229 pad = gst_element_get_static_pad (head, "sink");
2230 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2231 gst_object_unref (pad);
2232 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2239 if (!elem && !playsink->audio_sink) {
2240 post_missing_element_message (playsink, "autoaudiosink");
2241 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2242 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2243 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2244 (_("Both autoaudiosink and %s elements are missing."),
2245 DEFAULT_AUDIOSINK), (NULL));
2247 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2248 (_("The autoaudiosink element is missing.")), (NULL));
2251 if (playsink->audio_sink) {
2252 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2253 (_("Configured audiosink %s is not working."),
2254 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2255 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2256 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2257 (_("Both autoaudiosink and %s elements are not working."),
2258 DEFAULT_AUDIOSINK), (NULL));
2260 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2261 (_("The autoaudiosink element is not working.")), (NULL));
2264 free_chain ((GstPlayChain *) chain);
2269 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2270 (NULL), ("Failed to configure the audio sink."));
2271 /* checking sink made it READY */
2272 gst_element_set_state (chain->sink, GST_STATE_NULL);
2273 /* Remove chain from the bin to allow reuse later */
2274 gst_bin_remove (bin, chain->sink);
2275 free_chain ((GstPlayChain *) chain);
2281 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2284 GstPlayAudioChain *chain;
2285 GstStateChangeReturn ret;
2287 chain = playsink->audiochain;
2289 chain->chain.raw = raw;
2291 /* if the chain was active we don't do anything */
2292 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2295 /* try to set the sink element to READY again */
2296 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2297 if (ret == GST_STATE_CHANGE_FAILURE)
2300 /* find ts-offset element */
2301 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2302 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2305 /* check if the sink, or something within the sink, has the volume property.
2306 * If it does we don't need to add a volume element. */
2308 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2311 chain->volume = elem;
2313 if (playsink->volume_changed) {
2314 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2316 /* use the sink to control the volume */
2317 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2318 playsink->volume_changed = FALSE;
2321 g_signal_connect (chain->volume, "notify::volume",
2322 G_CALLBACK (notify_volume_cb), playsink);
2323 /* if the sink also has a mute property we can use this as well. We'll only
2324 * use the mute property if there is a volume property. We can simulate the
2325 * mute with the volume otherwise. */
2327 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2330 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2331 g_signal_connect (chain->mute, "notify::mute",
2332 G_CALLBACK (notify_mute_cb), playsink);
2335 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2337 GstPlaySinkAudioConvert *conv =
2338 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2340 /* no volume, we need to add a volume element when we can */
2341 g_object_set (chain->conv, "use-volume",
2342 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2343 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2345 /* Disconnect signals */
2346 disconnect_chain (chain, playsink);
2348 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2349 chain->volume = conv->volume;
2350 chain->mute = chain->volume;
2352 g_signal_connect (chain->volume, "notify::volume",
2353 G_CALLBACK (notify_volume_cb), playsink);
2355 g_signal_connect (chain->mute, "notify::mute",
2356 G_CALLBACK (notify_mute_cb), playsink);
2358 /* configure with the latest volume and mute */
2359 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2360 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2363 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2369 * +-------------------------------------------------------------------+
2371 * | +----------+ +------------+ +----------+ +-------+ |
2372 * | | visqueue | | audioconv | | audiores | | vis | |
2373 * | +-sink src-sink + samp src-sink src-sink src-+ |
2374 * | | +----------+ +------------+ +----------+ +-------+ | |
2376 * +-------------------------------------------------------------------+
2379 static GstPlayVisChain *
2380 gen_vis_chain (GstPlaySink * playsink)
2382 GstPlayVisChain *chain;
2388 chain = g_new0 (GstPlayVisChain, 1);
2389 chain->chain.playsink = playsink;
2391 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2393 chain->chain.bin = gst_bin_new ("visbin");
2394 bin = GST_BIN_CAST (chain->chain.bin);
2395 gst_object_ref_sink (bin);
2397 /* we're queuing raw audio here, we can remove this queue when we can disable
2398 * async behaviour in the video sink. */
2399 chain->queue = gst_element_factory_make ("queue", "visqueue");
2400 if (chain->queue == NULL)
2402 g_object_set (chain->queue, "silent", TRUE, NULL);
2403 gst_bin_add (bin, chain->queue);
2405 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2406 if (chain->conv == NULL)
2407 goto no_audioconvert;
2408 gst_bin_add (bin, chain->conv);
2410 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2411 if (chain->resample == NULL)
2412 goto no_audioresample;
2413 gst_bin_add (bin, chain->resample);
2415 /* this pad will be used for blocking the dataflow and switching the vis
2417 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2419 if (playsink->visualisation) {
2420 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2421 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2423 if (chain->vis == NULL) {
2424 GST_DEBUG_OBJECT (playsink, "trying goom");
2425 elem = gst_element_factory_make ("goom", "vis");
2426 chain->vis = try_element (playsink, elem, TRUE);
2428 if (chain->vis == NULL)
2431 gst_bin_add (bin, chain->vis);
2433 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2434 GST_PAD_LINK_CHECK_NOTHING);
2436 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2437 GST_PAD_LINK_CHECK_NOTHING);
2439 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2440 GST_PAD_LINK_CHECK_NOTHING);
2444 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2445 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2447 pad = gst_element_get_static_pad (chain->queue, "sink");
2448 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2449 gst_object_unref (pad);
2450 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2452 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2453 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2460 post_missing_element_message (playsink, "queue");
2461 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2462 (_("Missing element '%s' - check your GStreamer installation."),
2464 free_chain ((GstPlayChain *) chain);
2469 post_missing_element_message (playsink, "audioconvert");
2470 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2471 (_("Missing element '%s' - check your GStreamer installation."),
2472 "audioconvert"), ("possibly a liboil version mismatch?"));
2473 free_chain ((GstPlayChain *) chain);
2478 post_missing_element_message (playsink, "audioresample");
2479 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2480 (_("Missing element '%s' - check your GStreamer installation."),
2481 "audioresample"), (NULL));
2482 free_chain ((GstPlayChain *) chain);
2487 post_missing_element_message (playsink, "goom");
2488 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2489 (_("Missing element '%s' - check your GStreamer installation."),
2491 free_chain ((GstPlayChain *) chain);
2496 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2497 (NULL), ("Failed to configure the visualisation element."));
2498 /* element made it to READY */
2499 gst_element_set_state (chain->vis, GST_STATE_NULL);
2500 free_chain ((GstPlayChain *) chain);
2505 /* this function is called when all the request pads are requested and when we
2506 * have to construct the final pipeline. Based on the flags we construct the
2507 * final output pipelines.
2510 gst_play_sink_reconfigure (GstPlaySink * playsink)
2513 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2515 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2517 /* assume we need nothing */
2518 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2520 GST_PLAY_SINK_LOCK (playsink);
2521 GST_OBJECT_LOCK (playsink);
2522 /* get flags, there are protected with the object lock */
2523 flags = playsink->flags;
2524 GST_OBJECT_UNLOCK (playsink);
2526 /* figure out which components we need */
2527 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2528 /* we have subtitles and we are requested to show it */
2532 GST_OBJECT_LOCK (playsink);
2533 if (playsink->overlay_element)
2534 gst_object_unref (playsink->overlay_element);
2535 playsink->overlay_element = NULL;
2537 if (playsink->colorbalance_element) {
2538 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2539 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2540 gst_object_unref (playsink->colorbalance_element);
2542 playsink->colorbalance_element = NULL;
2543 GST_OBJECT_UNLOCK (playsink);
2545 if (((flags & GST_PLAY_FLAG_VIDEO)
2546 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2547 /* we have video and we are requested to show it */
2550 /* we only deinterlace if native video is not requested and
2551 * we have raw video */
2552 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2553 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2554 need_deinterlace = TRUE;
2557 if (playsink->audio_pad) {
2558 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2561 if (playsink->audio_pad_raw) {
2562 /* only can do vis with raw uncompressed audio */
2563 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2564 /* also add video when we add visualisation */
2571 /* we have a text_pad and we need text rendering, in this case we need a
2572 * video_pad to combine the video with the text or visualizations */
2573 if (need_text && !need_video) {
2574 if (playsink->video_pad) {
2576 } else if (need_audio) {
2577 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2578 (_("Can't play a text file without video or visualizations.")),
2579 ("Have text pad but no video pad or visualizations"));
2582 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2583 (_("Can't play a text file without video or visualizations.")),
2584 ("Have text pad but no video pad or visualizations"));
2585 GST_PLAY_SINK_UNLOCK (playsink);
2590 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2591 need_video, need_vis, need_text);
2593 /* set up video pipeline */
2595 gboolean raw, async;
2597 /* we need a raw sink when we do vis or when we have a raw pad */
2598 raw = need_vis ? TRUE : playsink->video_pad_raw;
2599 /* we try to set the sink async=FALSE when we need vis, this way we can
2600 * avoid a queue in the audio chain. */
2603 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2604 playsink->video_pad_raw);
2606 if (playsink->videochain) {
2607 /* try to reactivate the chain */
2608 if (!setup_video_chain (playsink, raw, async)) {
2609 if (playsink->video_sinkpad_stream_synchronizer) {
2610 gst_element_release_request_pad (GST_ELEMENT_CAST
2611 (playsink->stream_synchronizer),
2612 playsink->video_sinkpad_stream_synchronizer);
2613 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2614 playsink->video_sinkpad_stream_synchronizer = NULL;
2615 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2616 playsink->video_srcpad_stream_synchronizer = NULL;
2619 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2621 /* Remove the sink from the bin to keep its state
2622 * and unparent it to allow reuse */
2623 if (playsink->videochain->sink)
2624 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2625 playsink->videochain->sink);
2627 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2628 free_chain ((GstPlayChain *) playsink->videochain);
2629 playsink->videochain = NULL;
2633 if (!playsink->videochain)
2634 playsink->videochain = gen_video_chain (playsink, raw, async);
2635 if (!playsink->videochain)
2638 if (!playsink->video_sinkpad_stream_synchronizer) {
2639 GValue item = { 0, };
2642 playsink->video_sinkpad_stream_synchronizer =
2643 gst_element_get_request_pad (GST_ELEMENT_CAST
2644 (playsink->stream_synchronizer), "sink_%u");
2645 it = gst_pad_iterate_internal_links
2646 (playsink->video_sinkpad_stream_synchronizer);
2648 gst_iterator_next (it, &item);
2649 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2650 g_value_unset (&item);
2651 g_assert (playsink->video_srcpad_stream_synchronizer);
2652 gst_iterator_free (it);
2655 if (playsink->video_pad)
2656 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2657 playsink->video_sinkpad_stream_synchronizer);
2659 if (need_deinterlace) {
2660 if (!playsink->videodeinterlacechain)
2661 playsink->videodeinterlacechain =
2662 gen_video_deinterlace_chain (playsink);
2663 if (!playsink->videodeinterlacechain)
2666 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2668 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2670 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2671 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2673 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2674 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2676 if (playsink->videodeinterlacechain) {
2677 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2678 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2683 GST_DEBUG_OBJECT (playsink, "adding video chain");
2684 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2685 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2686 /* if we are not part of vis or subtitles, set the ghostpad target */
2687 if (!need_vis && !need_text && (!playsink->textchain
2688 || !playsink->text_pad)) {
2689 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2690 if (need_deinterlace)
2691 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2692 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2694 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2695 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2698 GST_DEBUG_OBJECT (playsink, "no video needed");
2699 if (playsink->videochain) {
2700 GST_DEBUG_OBJECT (playsink, "removing video chain");
2701 if (playsink->vischain) {
2704 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2706 /* also had visualisation, release the tee srcpad before we then
2707 * unlink the video from it */
2708 if (playsink->audio_tee_vissrc) {
2709 gst_element_release_request_pad (playsink->audio_tee,
2710 playsink->audio_tee_vissrc);
2711 gst_object_unref (playsink->audio_tee_vissrc);
2712 playsink->audio_tee_vissrc = NULL;
2715 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2716 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2719 if (playsink->video_sinkpad_stream_synchronizer) {
2720 gst_element_release_request_pad (GST_ELEMENT_CAST
2721 (playsink->stream_synchronizer),
2722 playsink->video_sinkpad_stream_synchronizer);
2723 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2724 playsink->video_sinkpad_stream_synchronizer = NULL;
2725 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2726 playsink->video_srcpad_stream_synchronizer = NULL;
2729 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2730 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2731 if (playsink->videochain->ts_offset)
2732 gst_object_unref (playsink->videochain->ts_offset);
2733 playsink->videochain->ts_offset = NULL;
2736 if (playsink->videodeinterlacechain) {
2737 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2738 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2741 if (playsink->video_pad)
2742 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2748 GST_DEBUG_OBJECT (playsink, "adding audio");
2750 /* get a raw sink if we are asked for a raw pad */
2751 raw = playsink->audio_pad_raw;
2753 if (playsink->audiochain) {
2754 /* try to reactivate the chain */
2755 if (!setup_audio_chain (playsink, raw)) {
2756 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2757 if (playsink->audio_tee_asrc) {
2758 gst_element_release_request_pad (playsink->audio_tee,
2759 playsink->audio_tee_asrc);
2760 gst_object_unref (playsink->audio_tee_asrc);
2761 playsink->audio_tee_asrc = NULL;
2764 if (playsink->audio_sinkpad_stream_synchronizer) {
2765 gst_element_release_request_pad (GST_ELEMENT_CAST
2766 (playsink->stream_synchronizer),
2767 playsink->audio_sinkpad_stream_synchronizer);
2768 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2769 playsink->audio_sinkpad_stream_synchronizer = NULL;
2770 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2771 playsink->audio_srcpad_stream_synchronizer = NULL;
2774 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2776 /* Remove the sink from the bin to keep its state
2777 * and unparent it to allow reuse */
2778 if (playsink->audiochain->sink)
2779 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2780 playsink->audiochain->sink);
2782 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2783 disconnect_chain (playsink->audiochain, playsink);
2784 playsink->audiochain->volume = NULL;
2785 playsink->audiochain->mute = NULL;
2786 if (playsink->audiochain->ts_offset)
2787 gst_object_unref (playsink->audiochain->ts_offset);
2788 playsink->audiochain->ts_offset = NULL;
2789 free_chain ((GstPlayChain *) playsink->audiochain);
2790 playsink->audiochain = NULL;
2791 playsink->volume_changed = playsink->mute_changed = FALSE;
2795 if (!playsink->audiochain) {
2796 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2797 playsink->audiochain = gen_audio_chain (playsink, raw);
2800 if (!playsink->audio_sinkpad_stream_synchronizer) {
2801 GValue item = { 0, };
2804 playsink->audio_sinkpad_stream_synchronizer =
2805 gst_element_get_request_pad (GST_ELEMENT_CAST
2806 (playsink->stream_synchronizer), "sink_%u");
2807 it = gst_pad_iterate_internal_links
2808 (playsink->audio_sinkpad_stream_synchronizer);
2810 gst_iterator_next (it, &item);
2811 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
2812 g_value_unset (&item);
2813 g_assert (playsink->audio_srcpad_stream_synchronizer);
2814 gst_iterator_free (it);
2817 if (playsink->audiochain) {
2818 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2819 if (playsink->audio_tee_asrc == NULL) {
2820 playsink->audio_tee_asrc =
2821 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2823 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2824 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2825 gst_pad_link_full (playsink->audio_tee_asrc,
2826 playsink->audio_sinkpad_stream_synchronizer,
2827 GST_PAD_LINK_CHECK_NOTHING);
2828 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2829 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2832 GST_DEBUG_OBJECT (playsink, "no audio needed");
2833 /* we have no audio or we are requested to not play audio */
2834 if (playsink->audiochain) {
2835 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2836 /* release the audio pad */
2837 if (playsink->audio_tee_asrc) {
2838 gst_element_release_request_pad (playsink->audio_tee,
2839 playsink->audio_tee_asrc);
2840 gst_object_unref (playsink->audio_tee_asrc);
2841 playsink->audio_tee_asrc = NULL;
2844 if (playsink->audio_sinkpad_stream_synchronizer) {
2845 gst_element_release_request_pad (GST_ELEMENT_CAST
2846 (playsink->stream_synchronizer),
2847 playsink->audio_sinkpad_stream_synchronizer);
2848 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2849 playsink->audio_sinkpad_stream_synchronizer = NULL;
2850 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2851 playsink->audio_srcpad_stream_synchronizer = NULL;
2854 if (playsink->audiochain->sink_volume) {
2855 disconnect_chain (playsink->audiochain, playsink);
2856 playsink->audiochain->volume = NULL;
2857 playsink->audiochain->mute = NULL;
2858 if (playsink->audiochain->ts_offset)
2859 gst_object_unref (playsink->audiochain->ts_offset);
2860 playsink->audiochain->ts_offset = NULL;
2862 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2863 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2870 if (!playsink->vischain)
2871 playsink->vischain = gen_vis_chain (playsink);
2873 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2875 if (playsink->vischain) {
2876 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2878 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2879 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2880 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2881 if (playsink->audio_tee_vissrc == NULL) {
2882 playsink->audio_tee_vissrc =
2883 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2885 gst_pad_link_full (playsink->audio_tee_vissrc,
2886 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2887 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2888 GST_PAD_LINK_CHECK_NOTHING);
2889 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2890 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2891 gst_object_unref (srcpad);
2894 GST_DEBUG_OBJECT (playsink, "no vis needed");
2895 if (playsink->vischain) {
2896 if (playsink->audio_tee_vissrc) {
2897 gst_element_release_request_pad (playsink->audio_tee,
2898 playsink->audio_tee_vissrc);
2899 gst_object_unref (playsink->audio_tee_vissrc);
2900 playsink->audio_tee_vissrc = NULL;
2902 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2903 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2904 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2909 GST_DEBUG_OBJECT (playsink, "adding text");
2910 if (!playsink->textchain) {
2911 GST_DEBUG_OBJECT (playsink, "creating text chain");
2912 playsink->textchain = gen_text_chain (playsink);
2914 if (playsink->textchain) {
2917 GST_DEBUG_OBJECT (playsink, "adding text chain");
2918 if (playsink->textchain->overlay)
2919 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2921 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2923 if (!playsink->text_sinkpad_stream_synchronizer) {
2924 GValue item = { 0, };
2926 playsink->text_sinkpad_stream_synchronizer =
2927 gst_element_get_request_pad (GST_ELEMENT_CAST
2928 (playsink->stream_synchronizer), "sink_%u");
2929 it = gst_pad_iterate_internal_links
2930 (playsink->text_sinkpad_stream_synchronizer);
2932 gst_iterator_next (it, &item);
2933 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
2934 g_value_unset (&item);
2935 g_assert (playsink->text_srcpad_stream_synchronizer);
2936 gst_iterator_free (it);
2938 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2939 playsink->text_sinkpad_stream_synchronizer);
2940 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2941 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2948 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2949 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2950 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2951 GST_PAD_LINK_CHECK_NOTHING);
2952 gst_object_unref (srcpad);
2954 if (need_deinterlace)
2955 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2956 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2958 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2959 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2961 gst_pad_link_full (playsink->textchain->srcpad,
2962 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2964 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2967 GST_DEBUG_OBJECT (playsink, "no text needed");
2968 /* we have no subtitles/text or we are requested to not show them */
2970 if (playsink->text_sinkpad_stream_synchronizer) {
2971 gst_element_release_request_pad (GST_ELEMENT_CAST
2972 (playsink->stream_synchronizer),
2973 playsink->text_sinkpad_stream_synchronizer);
2974 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2975 playsink->text_sinkpad_stream_synchronizer = NULL;
2976 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2977 playsink->text_srcpad_stream_synchronizer = NULL;
2980 if (playsink->textchain) {
2981 if (playsink->text_pad == NULL) {
2982 /* no text pad, remove the chain entirely */
2983 GST_DEBUG_OBJECT (playsink, "removing text chain");
2984 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2985 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2987 /* we have a chain and a textpad, turn the subtitles off */
2988 GST_DEBUG_OBJECT (playsink, "turning off the text");
2989 if (playsink->textchain->overlay)
2990 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2994 if (!need_video && playsink->video_pad) {
2995 if (playsink->video_sinkpad_stream_synchronizer) {
2996 gst_element_release_request_pad (GST_ELEMENT_CAST
2997 (playsink->stream_synchronizer),
2998 playsink->video_sinkpad_stream_synchronizer);
2999 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3000 playsink->video_sinkpad_stream_synchronizer = NULL;
3001 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3002 playsink->video_srcpad_stream_synchronizer = NULL;
3005 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3008 if (playsink->text_pad && !playsink->textchain)
3009 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3011 update_av_offset (playsink);
3012 do_async_done (playsink);
3013 GST_PLAY_SINK_UNLOCK (playsink);
3020 /* gen_ chain already posted error */
3021 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3022 GST_PLAY_SINK_UNLOCK (playsink);
3028 * gst_play_sink_set_flags:
3029 * @playsink: a #GstPlaySink
3030 * @flags: #GstPlayFlags
3032 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3033 * when constructing the sink pipelins.
3035 * Returns: TRUE if the flags could be configured.
3038 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3040 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3042 GST_OBJECT_LOCK (playsink);
3043 playsink->flags = flags;
3044 GST_OBJECT_UNLOCK (playsink);
3050 * gst_play_sink_get_flags:
3051 * @playsink: a #GstPlaySink
3053 * Get the flags of @playsink. That flags control the behaviour of the sink when
3054 * it constructs the sink pipelines.
3056 * Returns: the currently configured #GstPlayFlags.
3059 gst_play_sink_get_flags (GstPlaySink * playsink)
3063 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3065 GST_OBJECT_LOCK (playsink);
3066 res = playsink->flags;
3067 GST_OBJECT_UNLOCK (playsink);
3073 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3075 GstPlayTextChain *chain;
3077 GST_PLAY_SINK_LOCK (playsink);
3078 chain = (GstPlayTextChain *) playsink->textchain;
3079 g_free (playsink->font_desc);
3080 playsink->font_desc = g_strdup (desc);
3081 if (chain && chain->overlay) {
3082 g_object_set (chain->overlay, "font-desc", desc, NULL);
3084 GST_PLAY_SINK_UNLOCK (playsink);
3088 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3090 gchar *result = NULL;
3091 GstPlayTextChain *chain;
3093 GST_PLAY_SINK_LOCK (playsink);
3094 chain = (GstPlayTextChain *) playsink->textchain;
3095 if (chain && chain->overlay) {
3096 g_object_get (chain->overlay, "font-desc", &result, NULL);
3097 playsink->font_desc = g_strdup (result);
3099 result = g_strdup (playsink->font_desc);
3101 GST_PLAY_SINK_UNLOCK (playsink);
3107 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3108 const gchar * encoding)
3110 GstPlayTextChain *chain;
3112 GST_PLAY_SINK_LOCK (playsink);
3113 chain = (GstPlayTextChain *) playsink->textchain;
3114 g_free (playsink->subtitle_encoding);
3115 playsink->subtitle_encoding = g_strdup (encoding);
3116 if (chain && chain->overlay) {
3117 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3119 GST_PLAY_SINK_UNLOCK (playsink);
3123 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3125 gchar *result = NULL;
3126 GstPlayTextChain *chain;
3128 GST_PLAY_SINK_LOCK (playsink);
3129 chain = (GstPlayTextChain *) playsink->textchain;
3130 if (chain && chain->overlay) {
3131 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3132 playsink->subtitle_encoding = g_strdup (result);
3134 result = g_strdup (playsink->subtitle_encoding);
3136 GST_PLAY_SINK_UNLOCK (playsink);
3142 update_av_offset (GstPlaySink * playsink)
3145 GstPlayAudioChain *achain;
3146 GstPlayVideoChain *vchain;
3148 av_offset = playsink->av_offset;
3149 achain = (GstPlayAudioChain *) playsink->audiochain;
3150 vchain = (GstPlayVideoChain *) playsink->videochain;
3152 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3153 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3154 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3156 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3161 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3163 GST_PLAY_SINK_LOCK (playsink);
3164 playsink->av_offset = av_offset;
3165 update_av_offset (playsink);
3166 GST_PLAY_SINK_UNLOCK (playsink);
3170 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3174 GST_PLAY_SINK_LOCK (playsink);
3175 result = playsink->av_offset;
3176 GST_PLAY_SINK_UNLOCK (playsink);
3182 * gst_play_sink_get_last_sample:
3183 * @playsink: a #GstPlaySink
3185 * Get the last displayed sample from @playsink. This sample is in the native
3186 * format of the sink element, the caps in the result sample contain the format
3187 * of the frame data.
3189 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3193 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3195 GstSample *result = NULL;
3196 GstPlayVideoChain *chain;
3198 GST_PLAY_SINK_LOCK (playsink);
3199 GST_DEBUG_OBJECT (playsink, "taking last sample");
3200 /* get the video chain if we can */
3201 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3202 GST_DEBUG_OBJECT (playsink, "found video chain");
3203 /* see if the chain is active */
3204 if (chain->chain.activated && chain->sink) {
3207 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3209 /* find and get the last-buffer property now */
3211 gst_play_sink_find_property (playsink, chain->sink,
3212 "last-sample", GST_TYPE_SAMPLE))) {
3213 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3214 g_object_get (elem, "last-sample", &result, NULL);
3215 gst_object_unref (elem);
3219 GST_PLAY_SINK_UNLOCK (playsink);
3225 * gst_play_sink_convert_sample:
3226 * @playsink: a #GstPlaySink
3229 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3230 * be in the native format of the sink element and the caps on the buffer
3231 * describe the format of the frame. If @caps is not %NULL, the video
3232 * frame will be converted to the format of the caps.
3234 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3235 * available or when the conversion failed.
3238 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3243 result = gst_play_sink_get_last_sample (playsink);
3244 if (result != NULL && caps != NULL) {
3247 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3248 if (temp == NULL && err)
3251 gst_sample_unref (result);
3259 /* I'm really uncertain whether we should make playsink post an error
3260 * on the bus or not. It's not like it's a critical issue regarding
3261 * playsink behaviour. */
3262 GST_ERROR ("Error converting frame: %s", err->message);
3263 gst_sample_unref (result);
3270 is_raw_structure (GstStructure * s)
3274 name = gst_structure_get_name (s);
3276 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3282 is_raw_pad (GstPad * pad)
3284 GstPad *peer = gst_pad_get_peer (pad);
3286 gboolean raw = TRUE;
3291 caps = gst_pad_get_current_caps (peer);
3295 caps = gst_pad_query_caps (peer, NULL);
3297 n = gst_caps_get_size (caps);
3298 for (i = 0; i < n; i++) {
3299 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3303 } else if (raw != r) {
3304 GST_ERROR_OBJECT (pad,
3305 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3311 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3313 gst_caps_unref (caps);
3314 gst_object_unref (peer);
3319 static GstPadProbeReturn
3320 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3321 gpointer user_data);
3324 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3326 if (playsink->video_pad) {
3328 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3329 (playsink->video_pad)));
3330 if (blocked && playsink->video_block_id == 0) {
3331 playsink->video_block_id =
3332 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3333 sinkpad_blocked_cb, gst_object_ref (playsink),
3334 (GDestroyNotify) gst_object_unref);
3335 } else if (!blocked && playsink->video_block_id) {
3336 gst_pad_remove_probe (opad, playsink->video_block_id);
3337 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3338 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3339 playsink->video_block_id = 0;
3340 playsink->video_pad_blocked = FALSE;
3342 gst_object_unref (opad);
3347 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3349 if (playsink->audio_pad) {
3351 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3352 (playsink->audio_pad)));
3353 if (blocked && playsink->audio_block_id == 0) {
3354 playsink->audio_block_id =
3355 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3356 sinkpad_blocked_cb, gst_object_ref (playsink),
3357 (GDestroyNotify) gst_object_unref);
3358 } else if (!blocked && playsink->audio_block_id) {
3359 gst_pad_remove_probe (opad, playsink->audio_block_id);
3360 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3361 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3362 playsink->audio_block_id = 0;
3363 playsink->audio_pad_blocked = FALSE;
3365 gst_object_unref (opad);
3370 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3372 if (playsink->text_pad) {
3374 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3375 (playsink->text_pad)));
3376 if (blocked && playsink->text_block_id == 0) {
3377 playsink->text_block_id =
3378 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3379 sinkpad_blocked_cb, gst_object_ref (playsink),
3380 (GDestroyNotify) gst_object_unref);
3381 } else if (!blocked && playsink->text_block_id) {
3382 gst_pad_remove_probe (opad, playsink->text_block_id);
3383 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3384 playsink->text_block_id = 0;
3385 playsink->text_pad_blocked = FALSE;
3387 gst_object_unref (opad);
3391 static GstPadProbeReturn
3392 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3395 GstPlaySink *playsink = (GstPlaySink *) user_data;
3398 GST_PLAY_SINK_LOCK (playsink);
3400 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3401 if (pad == playsink->video_pad) {
3402 playsink->video_pad_blocked = TRUE;
3403 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3404 } else if (pad == playsink->audio_pad) {
3405 playsink->audio_pad_blocked = TRUE;
3406 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3407 } else if (pad == playsink->text_pad) {
3408 playsink->text_pad_blocked = TRUE;
3409 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3412 /* We reconfigure when for ALL streams:
3413 * * there isn't a pad
3414 * * OR the pad is blocked
3415 * * OR there are no pending blocks on that pad
3418 if ((!playsink->video_pad || playsink->video_pad_blocked
3419 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3420 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3421 && (!playsink->text_pad || playsink->text_pad_blocked
3422 || !PENDING_TEXT_BLOCK (playsink))) {
3423 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3425 if (playsink->video_pad) {
3426 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3427 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3428 playsink->video_pad_raw);
3431 if (playsink->audio_pad) {
3432 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3433 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3434 playsink->audio_pad_raw);
3437 gst_play_sink_reconfigure (playsink);
3439 video_set_blocked (playsink, FALSE);
3440 audio_set_blocked (playsink, FALSE);
3441 text_set_blocked (playsink, FALSE);
3444 gst_object_unref (pad);
3446 GST_PLAY_SINK_UNLOCK (playsink);
3448 return GST_PAD_PROBE_OK;
3452 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3454 gboolean reconfigure = FALSE;
3458 g_object_get (pad, "caps", &caps, NULL);
3462 if (pad == playsink->audio_pad) {
3463 raw = is_raw_pad (pad);
3464 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3465 && playsink->audiochain;
3466 GST_DEBUG_OBJECT (pad,
3467 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3469 } else if (pad == playsink->video_pad) {
3470 raw = is_raw_pad (pad);
3471 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3472 && playsink->videochain;
3473 GST_DEBUG_OBJECT (pad,
3474 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3478 gst_caps_unref (caps);
3481 GST_PLAY_SINK_LOCK (playsink);
3482 video_set_blocked (playsink, TRUE);
3483 audio_set_blocked (playsink, TRUE);
3484 text_set_blocked (playsink, TRUE);
3485 GST_PLAY_SINK_UNLOCK (playsink);
3490 * gst_play_sink_request_pad
3491 * @playsink: a #GstPlaySink
3492 * @type: a #GstPlaySinkType
3494 * Create or return a pad of @type.
3496 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3499 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3502 gboolean created = FALSE;
3503 gboolean activate = TRUE;
3504 const gchar *pad_name = NULL;
3505 gulong *block_id = NULL;
3507 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3509 GST_PLAY_SINK_LOCK (playsink);
3511 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3512 case GST_PLAY_SINK_TYPE_AUDIO:
3513 pad_name = "audio_sink";
3514 if (!playsink->audio_tee) {
3515 GST_LOG_OBJECT (playsink, "creating tee");
3516 /* create tee when needed. This element will feed the audio sink chain
3517 * and the vis chain. */
3518 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3519 if (playsink->audio_tee == NULL) {
3520 post_missing_element_message (playsink, "tee");
3521 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3522 (_("Missing element '%s' - check your GStreamer installation."),
3527 playsink->audio_tee_sink =
3528 gst_element_get_static_pad (playsink->audio_tee, "sink");
3529 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3530 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3533 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3535 if (!playsink->audio_pad) {
3536 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3537 playsink->audio_pad =
3538 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3539 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3540 G_CALLBACK (caps_notify_cb), playsink);
3543 playsink->audio_pad_raw = FALSE;
3544 res = playsink->audio_pad;
3545 block_id = &playsink->audio_block_id;
3547 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3548 case GST_PLAY_SINK_TYPE_VIDEO:
3549 pad_name = "video_sink";
3550 if (!playsink->video_pad) {
3551 GST_LOG_OBJECT (playsink, "ghosting videosink");
3552 playsink->video_pad =
3553 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3554 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3555 G_CALLBACK (caps_notify_cb), playsink);
3558 playsink->video_pad_raw = FALSE;
3559 res = playsink->video_pad;
3560 block_id = &playsink->video_block_id;
3562 case GST_PLAY_SINK_TYPE_TEXT:
3563 GST_LOG_OBJECT (playsink, "ghosting text");
3564 if (!playsink->text_pad) {
3565 playsink->text_pad =
3566 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3569 res = playsink->text_pad;
3570 block_id = &playsink->text_block_id;
3572 case GST_PLAY_SINK_TYPE_FLUSHING:
3576 /* we need a unique padname for the flushing pad. */
3577 padname = g_strdup_printf ("flushing_%u", playsink->count);
3578 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3589 GST_PLAY_SINK_UNLOCK (playsink);
3591 if (created && res) {
3592 /* we have to add the pad when it's active or we get an error when the
3593 * element is 'running' */
3594 gst_pad_set_active (res, TRUE);
3595 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3596 if (block_id && *block_id == 0) {
3598 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3601 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3602 sinkpad_blocked_cb, gst_object_ref (playsink),
3603 (GDestroyNotify) gst_object_unref);
3604 PENDING_FLAG_SET (playsink, type);
3605 gst_object_unref (blockpad);
3608 gst_pad_set_active (res, activate);
3615 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3616 const gchar * name, const GstCaps * caps)
3620 GstPlaySinkType type;
3621 const gchar *tplname;
3623 g_return_val_if_fail (templ != NULL, NULL);
3625 GST_DEBUG_OBJECT (element, "name:%s", name);
3627 psink = GST_PLAY_SINK (element);
3628 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3630 /* Figure out the GstPlaySinkType based on the template */
3631 if (!strcmp (tplname, "audio_sink"))
3632 type = GST_PLAY_SINK_TYPE_AUDIO;
3633 else if (!strcmp (tplname, "audio_raw_sink"))
3634 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3635 else if (!strcmp (tplname, "video_sink"))
3636 type = GST_PLAY_SINK_TYPE_VIDEO;
3637 else if (!strcmp (tplname, "video_raw_sink"))
3638 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3639 else if (!strcmp (tplname, "text_sink"))
3640 type = GST_PLAY_SINK_TYPE_TEXT;
3642 goto unknown_template;
3644 pad = gst_play_sink_request_pad (psink, type);
3648 GST_WARNING_OBJECT (element, "Unknown pad template");
3653 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3655 GstPad **res = NULL;
3656 gboolean untarget = TRUE;
3658 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3660 GST_PLAY_SINK_LOCK (playsink);
3661 if (pad == playsink->video_pad) {
3662 res = &playsink->video_pad;
3663 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3665 } else if (pad == playsink->audio_pad) {
3666 res = &playsink->audio_pad;
3667 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3669 } else if (pad == playsink->text_pad) {
3670 res = &playsink->text_pad;
3672 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3676 GST_PLAY_SINK_UNLOCK (playsink);
3679 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3680 gst_pad_set_active (*res, FALSE);
3682 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3683 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3685 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3686 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3692 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3694 GstPlaySink *psink = GST_PLAY_SINK (element);
3696 gst_play_sink_release_pad (psink, pad);
3700 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3702 GstPlaySink *playsink;
3704 playsink = GST_PLAY_SINK_CAST (bin);
3706 switch (GST_MESSAGE_TYPE (message)) {
3707 case GST_MESSAGE_STEP_DONE:
3712 gboolean flush, intermediate, eos;
3715 GST_INFO_OBJECT (playsink, "Handling step-done message");
3716 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3717 &intermediate, &duration, &eos);
3719 if (format == GST_FORMAT_BUFFERS) {
3720 /* for the buffer format, we align the other streams */
3721 if (playsink->audiochain) {
3725 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3728 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3729 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3733 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3736 case GST_MESSAGE_ELEMENT:{
3737 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
3738 GstVideoOverlay *overlay;
3740 GST_OBJECT_LOCK (playsink);
3741 if (playsink->overlay_element
3742 && GST_OBJECT_CAST (playsink->overlay_element) !=
3743 GST_MESSAGE_SRC (message)) {
3744 gst_object_unref (playsink->overlay_element);
3745 playsink->overlay_element = NULL;
3748 if (!playsink->overlay_element)
3749 playsink->overlay_element =
3750 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
3752 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
3753 GST_OBJECT_UNLOCK (playsink);
3755 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
3757 if (playsink->overlay_handle_set)
3758 gst_video_overlay_set_window_handle (playsink->overlay_element,
3759 playsink->overlay_handle);
3760 if (playsink->overlay_handle_events_set)
3761 gst_video_overlay_handle_events (playsink->overlay_element,
3762 playsink->overlay_handle_events);
3763 if (playsink->overlay_render_rectangle_set)
3764 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
3765 playsink->overlay_x, playsink->overlay_y,
3766 playsink->overlay_width, playsink->overlay_height);
3768 gst_object_unref (overlay);
3769 gst_message_unref (message);
3770 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
3775 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3780 /* Send an event to our sinks until one of them works; don't then send to the
3781 * remaining sinks (unlike GstBin)
3782 * Special case: If a text sink is set we need to send the event
3783 * to them in case it's source is different from the a/v stream's source.
3786 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3788 gboolean res = TRUE;
3789 if (playsink->textchain && playsink->textchain->sink) {
3790 gst_event_ref (event);
3791 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3792 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3794 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3798 if (playsink->videochain) {
3799 gst_event_ref (event);
3800 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3801 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3804 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3806 if (playsink->audiochain) {
3807 gst_event_ref (event);
3808 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3809 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3812 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3816 gst_event_unref (event);
3820 /* We only want to send the event to a single sink (overriding GstBin's
3821 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3822 * events appropriately. So, this is a messy duplication of code. */
3824 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3826 gboolean res = FALSE;
3827 GstEventType event_type = GST_EVENT_TYPE (event);
3828 GstPlaySink *playsink;
3829 playsink = GST_PLAY_SINK_CAST (element);
3830 switch (event_type) {
3831 case GST_EVENT_SEEK:
3832 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3833 res = gst_play_sink_send_event_to_sink (playsink, event);
3835 case GST_EVENT_STEP:
3840 gboolean flush, intermediate;
3841 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3843 if (format == GST_FORMAT_BUFFERS) {
3844 /* for buffers, we will try to step video frames, for other formats we
3845 * send the step to all sinks */
3846 res = gst_play_sink_send_event_to_sink (playsink, event);
3849 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3856 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3863 static GstStateChangeReturn
3864 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3866 GstStateChangeReturn ret;
3867 GstStateChangeReturn bret;
3868 GstPlaySink *playsink;
3869 playsink = GST_PLAY_SINK (element);
3870 switch (transition) {
3871 case GST_STATE_CHANGE_READY_TO_PAUSED:
3872 playsink->need_async_start = TRUE;
3873 /* we want to go async to PAUSED until we managed to configure and add the
3875 do_async_start (playsink);
3876 ret = GST_STATE_CHANGE_ASYNC;
3878 case GST_STATE_CHANGE_PAUSED_TO_READY:
3879 /* unblock all pads here */
3880 GST_PLAY_SINK_LOCK (playsink);
3881 video_set_blocked (playsink, FALSE);
3882 audio_set_blocked (playsink, FALSE);
3883 text_set_blocked (playsink, FALSE);
3884 GST_PLAY_SINK_UNLOCK (playsink);
3886 case GST_STATE_CHANGE_READY_TO_NULL:
3887 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3888 /* remove our links to the mute and volume elements when they were
3889 * provided by a sink */
3890 disconnect_chain (playsink->audiochain, playsink);
3891 playsink->audiochain->volume = NULL;
3892 playsink->audiochain->mute = NULL;
3895 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3896 gst_object_unref (playsink->audiochain->ts_offset);
3897 playsink->audiochain->ts_offset = NULL;
3900 if (playsink->videochain && playsink->videochain->ts_offset) {
3901 gst_object_unref (playsink->videochain->ts_offset);
3902 playsink->videochain->ts_offset = NULL;
3905 GST_OBJECT_LOCK (playsink);
3906 if (playsink->overlay_element)
3907 gst_object_unref (playsink->overlay_element);
3908 playsink->overlay_element = NULL;
3910 if (playsink->colorbalance_element) {
3911 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3912 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3913 gst_object_unref (playsink->colorbalance_element);
3915 playsink->colorbalance_element = NULL;
3916 GST_OBJECT_UNLOCK (playsink);
3918 ret = GST_STATE_CHANGE_SUCCESS;
3921 /* all other state changes return SUCCESS by default, this value can be
3922 * overridden by the result of the children */
3923 ret = GST_STATE_CHANGE_SUCCESS;
3927 /* do the state change of the children */
3929 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3931 /* now look at the result of our children and adjust the return value */
3933 case GST_STATE_CHANGE_FAILURE:
3934 /* failure, we stop */
3935 goto activate_failed;
3936 case GST_STATE_CHANGE_NO_PREROLL:
3937 /* some child returned NO_PREROLL. This is strange but we never know. We
3938 * commit our async state change (if any) and return the NO_PREROLL */
3939 do_async_done (playsink);
3942 case GST_STATE_CHANGE_ASYNC:
3943 /* some child was async, return this */
3947 /* return our previously configured return value */
3951 switch (transition) {
3952 case GST_STATE_CHANGE_READY_TO_PAUSED:
3954 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3955 /* FIXME Release audio device when we implement that */
3956 playsink->need_async_start = TRUE;
3958 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3959 if (playsink->video_sinkpad_stream_synchronizer) {
3960 gst_element_release_request_pad (GST_ELEMENT_CAST
3961 (playsink->stream_synchronizer),
3962 playsink->video_sinkpad_stream_synchronizer);
3963 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3964 playsink->video_sinkpad_stream_synchronizer = NULL;
3965 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3966 playsink->video_srcpad_stream_synchronizer = NULL;
3968 if (playsink->audio_sinkpad_stream_synchronizer) {
3969 gst_element_release_request_pad (GST_ELEMENT_CAST
3970 (playsink->stream_synchronizer),
3971 playsink->audio_sinkpad_stream_synchronizer);
3972 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3973 playsink->audio_sinkpad_stream_synchronizer = NULL;
3974 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3975 playsink->audio_srcpad_stream_synchronizer = NULL;
3977 if (playsink->text_sinkpad_stream_synchronizer) {
3978 gst_element_release_request_pad (GST_ELEMENT_CAST
3979 (playsink->stream_synchronizer),
3980 playsink->text_sinkpad_stream_synchronizer);
3981 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3982 playsink->text_sinkpad_stream_synchronizer = NULL;
3983 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3984 playsink->text_srcpad_stream_synchronizer = NULL;
3988 case GST_STATE_CHANGE_READY_TO_NULL:
3989 /* remove sinks we added */
3990 if (playsink->videodeinterlacechain) {
3991 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3993 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3995 if (playsink->videochain) {
3996 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3997 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3999 if (playsink->audiochain) {
4000 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4001 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4003 if (playsink->vischain) {
4004 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4005 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4007 if (playsink->textchain) {
4008 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4009 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4011 do_async_done (playsink);
4012 /* when going to READY, keep elements around as long as possible,
4013 * so they may be re-used faster next time/url around.
4014 * when really going to NULL, clean up everything completely. */
4015 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4017 /* Unparent the sinks to allow reuse */
4018 if (playsink->videochain && playsink->videochain->sink)
4019 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4020 playsink->videochain->sink);
4021 if (playsink->audiochain && playsink->audiochain->sink)
4022 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4023 playsink->audiochain->sink);
4024 if (playsink->textchain && playsink->textchain->sink)
4025 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4026 playsink->textchain->sink);
4027 if (playsink->audio_sink != NULL)
4028 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4029 if (playsink->video_sink != NULL)
4030 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4031 if (playsink->visualisation != NULL)
4032 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4033 if (playsink->text_sink != NULL)
4034 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4035 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4036 playsink->videodeinterlacechain = NULL;
4037 free_chain ((GstPlayChain *) playsink->videochain);
4038 playsink->videochain = NULL;
4039 free_chain ((GstPlayChain *) playsink->audiochain);
4040 playsink->audiochain = NULL;
4041 free_chain ((GstPlayChain *) playsink->vischain);
4042 playsink->vischain = NULL;
4043 free_chain ((GstPlayChain *) playsink->textchain);
4044 playsink->textchain = NULL;
4054 GST_DEBUG_OBJECT (element,
4055 "element failed to change states -- activation problem?");
4056 return GST_STATE_CHANGE_FAILURE;
4061 gst_play_sink_set_property (GObject * object, guint prop_id,
4062 const GValue * value, GParamSpec * spec)
4064 GstPlaySink *playsink = GST_PLAY_SINK (object);
4067 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4070 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4073 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4075 case PROP_FONT_DESC:
4076 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4078 case PROP_SUBTITLE_ENCODING:
4079 gst_play_sink_set_subtitle_encoding (playsink,
4080 g_value_get_string (value));
4082 case PROP_VIS_PLUGIN:
4083 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4085 case PROP_AV_OFFSET:
4086 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4088 case PROP_VIDEO_SINK:
4089 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4090 g_value_get_object (value));
4092 case PROP_AUDIO_SINK:
4093 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4094 g_value_get_object (value));
4096 case PROP_TEXT_SINK:
4097 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4098 g_value_get_object (value));
4101 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4107 gst_play_sink_get_property (GObject * object, guint prop_id,
4108 GValue * value, GParamSpec * spec)
4110 GstPlaySink *playsink = GST_PLAY_SINK (object);
4113 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4116 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4119 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4121 case PROP_FONT_DESC:
4122 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4124 case PROP_SUBTITLE_ENCODING:
4125 g_value_take_string (value,
4126 gst_play_sink_get_subtitle_encoding (playsink));
4128 case PROP_VIS_PLUGIN:
4129 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4132 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4134 case PROP_AV_OFFSET:
4135 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4137 case PROP_VIDEO_SINK:
4138 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4139 GST_PLAY_SINK_TYPE_VIDEO));
4141 case PROP_AUDIO_SINK:
4142 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4143 GST_PLAY_SINK_TYPE_AUDIO));
4145 case PROP_TEXT_SINK:
4146 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4147 GST_PLAY_SINK_TYPE_TEXT));
4150 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4156 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4158 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4159 GstVideoOverlay *overlay_element;
4161 GST_OBJECT_LOCK (playsink);
4162 if (playsink->overlay_element)
4164 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4166 overlay_element = NULL;
4167 GST_OBJECT_UNLOCK (playsink);
4169 if (overlay_element) {
4170 gst_video_overlay_expose (overlay_element);
4171 gst_object_unref (overlay_element);
4176 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4177 gboolean handle_events)
4179 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4180 GstVideoOverlay *overlay_element;
4182 GST_OBJECT_LOCK (playsink);
4183 if (playsink->overlay_element)
4185 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4187 overlay_element = NULL;
4188 GST_OBJECT_UNLOCK (playsink);
4190 playsink->overlay_handle_events_set = TRUE;
4191 playsink->overlay_handle_events = handle_events;
4193 if (overlay_element) {
4194 gst_video_overlay_handle_events (overlay_element, handle_events);
4195 gst_object_unref (overlay_element);
4200 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4201 gint y, gint width, gint height)
4203 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4204 GstVideoOverlay *overlay_element;
4206 GST_OBJECT_LOCK (playsink);
4207 if (playsink->overlay_element)
4209 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4211 overlay_element = NULL;
4212 GST_OBJECT_UNLOCK (playsink);
4214 playsink->overlay_render_rectangle_set = TRUE;
4215 playsink->overlay_x = x;
4216 playsink->overlay_y = y;
4217 playsink->overlay_width = width;
4218 playsink->overlay_height = height;
4220 if (overlay_element) {
4221 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4223 gst_object_unref (overlay_element);
4228 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4231 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4232 GstVideoOverlay *overlay_element;
4234 GST_OBJECT_LOCK (playsink);
4235 if (playsink->overlay_element)
4237 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4239 overlay_element = NULL;
4240 GST_OBJECT_UNLOCK (playsink);
4242 playsink->overlay_handle_set = TRUE;
4243 playsink->overlay_handle = handle;
4245 if (overlay_element) {
4246 gst_video_overlay_set_window_handle (overlay_element, handle);
4247 gst_object_unref (overlay_element);
4252 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4254 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4255 iface->expose = gst_play_sink_overlay_expose;
4256 iface->handle_events = gst_play_sink_overlay_handle_events;
4257 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4258 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4262 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4263 GstStructure * structure)
4265 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4268 GST_PLAY_SINK_LOCK (playsink);
4269 if (playsink->videochain && playsink->videochain->chain.bin)
4270 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4271 GST_PLAY_SINK_UNLOCK (playsink);
4274 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4277 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4279 gst_object_unref (nav);
4282 gst_object_unref (bin);
4286 gst_structure_free (structure);
4290 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4292 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4294 iface->send_event = gst_play_sink_navigation_send_event;
4297 static const GList *
4298 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4300 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4302 return playsink->colorbalance_channels;
4306 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4307 GstColorBalanceChannel * proxy, gint value)
4309 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4312 GstColorBalance *balance_element = NULL;
4314 GST_OBJECT_LOCK (playsink);
4315 if (playsink->colorbalance_element)
4317 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4318 GST_OBJECT_UNLOCK (playsink);
4320 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4321 GstColorBalanceChannel *proxy_tmp = l->data;
4324 if (proxy_tmp != proxy)
4327 playsink->colorbalance_values[i] = value;
4329 if (balance_element) {
4330 GstColorBalanceChannel *channel = NULL;
4331 const GList *channels, *k;
4333 channels = gst_color_balance_list_channels (balance_element);
4334 for (k = channels; k; k = k->next) {
4335 GstColorBalanceChannel *tmp = l->data;
4337 if (g_strrstr (tmp->label, proxy->label)) {
4345 /* Convert to [0, 1] range */
4348 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4349 (gdouble) proxy->min_value);
4350 /* Convert to channel range */
4352 channel->min_value + new_val * ((gdouble) channel->max_value -
4353 (gdouble) channel->min_value);
4355 gst_color_balance_set_value (balance_element, channel,
4356 (gint) (new_val + 0.5));
4358 gst_object_unref (balance_element);
4361 gst_color_balance_value_changed (balance, proxy, value);
4367 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4368 GstColorBalanceChannel * proxy)
4370 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4374 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4375 GstColorBalanceChannel *proxy_tmp = l->data;
4377 if (proxy_tmp != proxy)
4380 return playsink->colorbalance_values[i];
4383 g_return_val_if_reached (0);
4386 static GstColorBalanceType
4387 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4389 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4390 GstColorBalance *balance_element = NULL;
4391 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4393 GST_OBJECT_LOCK (playsink);
4394 if (playsink->colorbalance_element)
4396 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4397 GST_OBJECT_UNLOCK (playsink);
4399 if (balance_element) {
4400 t = gst_color_balance_get_balance_type (balance_element);
4401 gst_object_unref (balance_element);
4408 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4410 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4412 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4413 iface->set_value = gst_play_sink_colorbalance_set_value;
4414 iface->get_value = gst_play_sink_colorbalance_get_value;
4415 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4419 gst_play_sink_plugin_init (GstPlugin * plugin)
4421 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4422 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4423 GST_TYPE_PLAY_SINK);