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.
25 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
26 * with newer GLib versions (>= 2.31.0) */
27 #define GLIB_DISABLE_DEPRECATION_WARNINGS
32 #include <gst/gst-i18n-plugin.h>
33 #include <gst/pbutils/pbutils.h>
34 #include <gst/video/video.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
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_static_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_static_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)
151 GStaticRecMutex lock;
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 */
211 struct _GstPlaySinkClass
213 GstBinClass parent_class;
215 gboolean (*reconfigure) (GstPlaySink * playsink);
217 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
221 static GstStaticPadTemplate audiotemplate =
222 GST_STATIC_PAD_TEMPLATE ("audio_sink",
225 GST_STATIC_CAPS_ANY);
226 static GstStaticPadTemplate videotemplate =
227 GST_STATIC_PAD_TEMPLATE ("video_sink",
230 GST_STATIC_CAPS_ANY);
231 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
234 GST_STATIC_CAPS_ANY);
236 /* FIXME 0.11: Remove */
237 static GstStaticPadTemplate audiorawtemplate =
238 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
241 GST_STATIC_CAPS_ANY);
242 static GstStaticPadTemplate videorawtemplate =
243 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
246 GST_STATIC_CAPS_ANY);
257 PROP_SUBTITLE_ENCODING,
273 static void gst_play_sink_dispose (GObject * object);
274 static void gst_play_sink_finalize (GObject * object);
275 static void gst_play_sink_set_property (GObject * object, guint prop_id,
276 const GValue * value, GParamSpec * spec);
277 static void gst_play_sink_get_property (GObject * object, guint prop_id,
278 GValue * value, GParamSpec * spec);
280 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
281 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
282 static void gst_play_sink_release_request_pad (GstElement * element,
284 static gboolean gst_play_sink_send_event (GstElement * element,
286 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
287 GstStateChange transition);
289 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
291 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
292 GstPlaySink * playsink);
293 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
294 GstPlaySink * playsink);
296 static void update_av_offset (GstPlaySink * playsink);
299 gst_play_marshal_SAMPLE__BOXED (GClosure * closure,
300 GValue * return_value G_GNUC_UNUSED,
301 guint n_param_values,
302 const GValue * param_values,
303 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
305 typedef GstSample *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
306 gpointer arg_1, gpointer data2);
307 register GMarshalFunc_OBJECT__BOXED callback;
308 register GCClosure *cc = (GCClosure *) closure;
309 register gpointer data1, data2;
311 g_return_if_fail (return_value != NULL);
312 g_return_if_fail (n_param_values == 2);
314 if (G_CCLOSURE_SWAP_DATA (closure)) {
315 data1 = closure->data;
316 data2 = g_value_peek_pointer (param_values + 0);
318 data1 = g_value_peek_pointer (param_values + 0);
319 data2 = closure->data;
322 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
324 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
326 gst_value_take_sample (return_value, v_return);
329 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
331 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
334 gst_play_sink_class_init (GstPlaySinkClass * klass)
336 GObjectClass *gobject_klass;
337 GstElementClass *gstelement_klass;
338 GstBinClass *gstbin_klass;
340 gobject_klass = (GObjectClass *) klass;
341 gstelement_klass = (GstElementClass *) klass;
342 gstbin_klass = (GstBinClass *) klass;
344 gobject_klass->dispose = gst_play_sink_dispose;
345 gobject_klass->finalize = gst_play_sink_finalize;
346 gobject_klass->set_property = gst_play_sink_set_property;
347 gobject_klass->get_property = gst_play_sink_get_property;
353 * Control the behaviour of playsink.
355 g_object_class_install_property (gobject_klass, PROP_FLAGS,
356 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
357 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
358 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
361 * GstPlaySink:volume:
363 * Get or set the current audio stream volume. 1.0 means 100%,
364 * 0.0 means mute. This uses a linear volume scale.
367 g_object_class_install_property (gobject_klass, PROP_VOLUME,
368 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
369 0.0, VOLUME_MAX_DOUBLE, 1.0,
370 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
371 g_object_class_install_property (gobject_klass, PROP_MUTE,
372 g_param_spec_boolean ("mute", "Mute",
373 "Mute the audio channel without changing the volume", FALSE,
374 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
375 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
376 g_param_spec_string ("subtitle-font-desc",
377 "Subtitle font description",
378 "Pango font description of font "
379 "to be used for subtitle rendering", NULL,
380 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
381 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
382 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
383 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
384 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
385 "be checked for an encoding to use. If that is not set either, "
386 "ISO-8859-15 will be assumed.", NULL,
387 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
388 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
389 g_param_spec_object ("vis-plugin", "Vis plugin",
390 "the visualization element to use (NULL = default)",
391 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
393 * GstPlaySink:sample:
395 * Get the currently rendered or prerolled sample in the video sink.
396 * The #GstCaps in the sample will describe the format of the buffer.
398 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
399 g_param_spec_boxed ("sample", "Sample",
400 "The last sample (NULL = no video available)",
401 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
403 * GstPlaySink:av-offset:
405 * Control the synchronisation offset between the audio and video streams.
406 * Positive values make the audio ahead of the video and negative values make
407 * the audio go behind the video.
411 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
412 g_param_spec_int64 ("av-offset", "AV Offset",
413 "The synchronisation offset between audio and video in nanoseconds",
414 G_MININT64, G_MAXINT64, 0,
415 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
418 * GstPlaySink:video-sink:
420 * Set the used video sink element. NULL will use the default sink. playsink
421 * must be in %GST_STATE_NULL
425 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
426 g_param_spec_object ("video-sink", "Video Sink",
427 "the video output element to use (NULL = default sink)",
428 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
430 * GstPlaySink:audio-sink:
432 * Set the used audio sink element. NULL will use the default sink. playsink
433 * must be in %GST_STATE_NULL
437 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
438 g_param_spec_object ("audio-sink", "Audio Sink",
439 "the audio output element to use (NULL = default sink)",
440 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
442 * GstPlaySink:text-sink:
444 * Set the used text sink element. NULL will use the default sink. playsink
445 * must be in %GST_STATE_NULL
449 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
450 g_param_spec_object ("text-sink", "Text sink",
451 "the text output element to use (NULL = default textoverlay)",
452 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
455 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
456 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
457 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
460 * GstPlaySink::convert-sample
461 * @playsink: a #GstPlaySink
462 * @caps: the target format of the sample
464 * Action signal to retrieve the currently playing video sample in the format
465 * specified by @caps.
466 * If @caps is %NULL, no conversion will be performed and this function is
467 * equivalent to the #GstPlaySink::sample property.
469 * Returns: a #GstSample of the current video sample converted to #caps.
470 * The caps in the sample will describe the final layout of the buffer data.
471 * %NULL is returned when no current sample can be retrieved or when the
474 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
475 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
476 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
477 gst_play_marshal_SAMPLE__BOXED, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
479 gst_element_class_add_pad_template (gstelement_klass,
480 gst_static_pad_template_get (&audiorawtemplate));
481 gst_element_class_add_pad_template (gstelement_klass,
482 gst_static_pad_template_get (&audiotemplate));
483 gst_element_class_add_pad_template (gstelement_klass,
484 gst_static_pad_template_get (&videorawtemplate));
485 gst_element_class_add_pad_template (gstelement_klass,
486 gst_static_pad_template_get (&videotemplate));
487 gst_element_class_add_pad_template (gstelement_klass,
488 gst_static_pad_template_get (&texttemplate));
489 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
491 "Convenience sink for multiple streams",
492 "Wim Taymans <wim.taymans@gmail.com>");
494 gstelement_klass->change_state =
495 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
496 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
497 gstelement_klass->request_new_pad =
498 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
499 gstelement_klass->release_pad =
500 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
502 gstbin_klass->handle_message =
503 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
505 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
506 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
510 gst_play_sink_init (GstPlaySink * playsink)
513 playsink->video_sink = NULL;
514 playsink->audio_sink = NULL;
515 playsink->visualisation = NULL;
516 playsink->text_sink = NULL;
517 playsink->volume = 1.0;
518 playsink->font_desc = NULL;
519 playsink->subtitle_encoding = NULL;
520 playsink->flags = DEFAULT_FLAGS;
522 playsink->stream_synchronizer =
523 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
524 gst_bin_add (GST_BIN_CAST (playsink),
525 GST_ELEMENT_CAST (playsink->stream_synchronizer));
527 g_static_rec_mutex_init (&playsink->lock);
528 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
532 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
536 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
539 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
545 free_chain (GstPlayChain * chain)
549 gst_object_unref (chain->bin);
555 gst_play_sink_dispose (GObject * object)
557 GstPlaySink *playsink;
559 playsink = GST_PLAY_SINK (object);
561 if (playsink->audio_sink != NULL) {
562 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
563 gst_object_unref (playsink->audio_sink);
564 playsink->audio_sink = NULL;
566 if (playsink->video_sink != NULL) {
567 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
568 gst_object_unref (playsink->video_sink);
569 playsink->video_sink = NULL;
571 if (playsink->visualisation != NULL) {
572 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
573 gst_object_unref (playsink->visualisation);
574 playsink->visualisation = NULL;
576 if (playsink->text_sink != NULL) {
577 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
578 gst_object_unref (playsink->text_sink);
579 playsink->text_sink = NULL;
582 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
583 playsink->videodeinterlacechain = NULL;
584 free_chain ((GstPlayChain *) playsink->videochain);
585 playsink->videochain = NULL;
586 free_chain ((GstPlayChain *) playsink->audiochain);
587 playsink->audiochain = NULL;
588 free_chain ((GstPlayChain *) playsink->vischain);
589 playsink->vischain = NULL;
590 free_chain ((GstPlayChain *) playsink->textchain);
591 playsink->textchain = NULL;
593 if (playsink->audio_tee_sink) {
594 gst_object_unref (playsink->audio_tee_sink);
595 playsink->audio_tee_sink = NULL;
598 if (playsink->audio_tee_vissrc) {
599 gst_element_release_request_pad (playsink->audio_tee,
600 playsink->audio_tee_vissrc);
601 gst_object_unref (playsink->audio_tee_vissrc);
602 playsink->audio_tee_vissrc = NULL;
605 if (playsink->audio_tee_asrc) {
606 gst_element_release_request_pad (playsink->audio_tee,
607 playsink->audio_tee_asrc);
608 gst_object_unref (playsink->audio_tee_asrc);
609 playsink->audio_tee_asrc = NULL;
612 g_free (playsink->font_desc);
613 playsink->font_desc = NULL;
615 g_free (playsink->subtitle_encoding);
616 playsink->subtitle_encoding = NULL;
618 playsink->stream_synchronizer = NULL;
620 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
624 gst_play_sink_finalize (GObject * object)
626 GstPlaySink *playsink;
628 playsink = GST_PLAY_SINK (object);
630 g_static_rec_mutex_free (&playsink->lock);
632 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
636 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
639 GstElement **elem = NULL, *old = NULL;
641 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
643 GST_PLAY_SINK_LOCK (playsink);
645 case GST_PLAY_SINK_TYPE_AUDIO:
646 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
647 elem = &playsink->audio_sink;
649 case GST_PLAY_SINK_TYPE_VIDEO:
650 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
651 elem = &playsink->video_sink;
653 case GST_PLAY_SINK_TYPE_TEXT:
654 elem = &playsink->text_sink;
662 gst_object_ref (sink);
665 GST_PLAY_SINK_UNLOCK (playsink);
669 gst_element_set_state (old, GST_STATE_NULL);
670 gst_object_unref (old);
675 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
677 GstElement *result = NULL;
678 GstElement *elem = NULL, *chainp = NULL;
680 GST_PLAY_SINK_LOCK (playsink);
682 case GST_PLAY_SINK_TYPE_AUDIO:
683 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
685 GstPlayAudioChain *chain;
686 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
687 chainp = chain->sink;
688 elem = playsink->audio_sink;
691 case GST_PLAY_SINK_TYPE_VIDEO:
692 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
694 GstPlayVideoChain *chain;
695 if ((chain = (GstPlayVideoChain *) playsink->videochain))
696 chainp = chain->sink;
697 elem = playsink->video_sink;
700 case GST_PLAY_SINK_TYPE_TEXT:
702 GstPlayTextChain *chain;
703 if ((chain = (GstPlayTextChain *) playsink->textchain))
704 chainp = chain->sink;
705 elem = playsink->text_sink;
712 /* we have an active chain with a sink, get the sink */
713 result = gst_object_ref (chainp);
715 /* nothing found, return last configured sink */
716 if (result == NULL && elem)
717 result = gst_object_ref (elem);
718 GST_PLAY_SINK_UNLOCK (playsink);
723 static GstPadProbeReturn
724 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
727 GstPlaySink *playsink;
728 GstPlayVisChain *chain;
730 playsink = GST_PLAY_SINK (user_data);
732 GST_PLAY_SINK_LOCK (playsink);
733 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
734 /* now try to change the plugin in the running vis chain */
735 if (!(chain = (GstPlayVisChain *) playsink->vischain))
738 /* unlink the old plugin and unghost the pad */
739 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
740 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
742 /* set the old plugin to NULL and remove */
743 gst_element_set_state (chain->vis, GST_STATE_NULL);
744 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
746 /* add new plugin and set state to playing */
747 chain->vis = playsink->visualisation;
748 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
749 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
752 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
753 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
756 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
757 GST_PAD_LINK_CHECK_NOTHING);
758 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
762 GST_PLAY_SINK_UNLOCK (playsink);
764 /* remove the probe and unblock the pad */
765 return GST_PAD_PROBE_REMOVE;
769 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
771 GstPlayVisChain *chain;
773 /* setting NULL means creating the default vis plugin */
775 vis = gst_element_factory_make ("goom", "vis");
777 /* simply return if we don't have a vis plugin here */
781 GST_PLAY_SINK_LOCK (playsink);
782 /* first store the new vis */
783 if (playsink->visualisation)
784 gst_object_unref (playsink->visualisation);
786 gst_object_ref_sink (vis);
787 playsink->visualisation = vis;
789 /* now try to change the plugin in the running vis chain, if we have no chain,
790 * we don't bother, any future vis chain will be created with the new vis
792 if (!(chain = (GstPlayVisChain *) playsink->vischain))
795 /* block the pad, the next time the callback is called we can change the
796 * visualisation. It's possible that this never happens or that the pad was
797 * already blocked. If the callback never happens, we don't have new data so
798 * we don't need the new vis plugin. If the pad was already blocked, the
799 * function returns FALSE but the previous pad block will do the right thing
801 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
802 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
803 gst_play_sink_vis_blocked, playsink, NULL);
805 GST_PLAY_SINK_UNLOCK (playsink);
811 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
813 GstElement *result = NULL;
814 GstPlayVisChain *chain;
816 GST_PLAY_SINK_LOCK (playsink);
817 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
818 /* we have an active chain, get the sink */
820 result = gst_object_ref (chain->vis);
822 /* nothing found, return last configured sink */
823 if (result == NULL && playsink->visualisation)
824 result = gst_object_ref (playsink->visualisation);
825 GST_PLAY_SINK_UNLOCK (playsink);
831 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
833 GstPlayAudioChain *chain;
835 GST_PLAY_SINK_LOCK (playsink);
836 playsink->volume = volume;
837 chain = (GstPlayAudioChain *) playsink->audiochain;
838 if (chain && chain->volume) {
839 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
840 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
841 chain->mute, volume, playsink->mute);
842 /* if there is a mute element or we are not muted, set the volume */
843 if (chain->mute || !playsink->mute)
844 g_object_set (chain->volume, "volume", volume, NULL);
846 GST_LOG_OBJECT (playsink, "no volume element");
847 playsink->volume_changed = TRUE;
849 GST_PLAY_SINK_UNLOCK (playsink);
853 gst_play_sink_get_volume (GstPlaySink * playsink)
856 GstPlayAudioChain *chain;
858 GST_PLAY_SINK_LOCK (playsink);
859 chain = (GstPlayAudioChain *) playsink->audiochain;
860 result = playsink->volume;
861 if (chain && chain->volume) {
862 if (chain->mute || !playsink->mute) {
863 g_object_get (chain->volume, "volume", &result, NULL);
864 playsink->volume = result;
867 GST_PLAY_SINK_UNLOCK (playsink);
873 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
875 GstPlayAudioChain *chain;
877 GST_PLAY_SINK_LOCK (playsink);
878 playsink->mute = mute;
879 chain = (GstPlayAudioChain *) playsink->audiochain;
882 g_object_set (chain->mute, "mute", mute, NULL);
883 } else if (chain->volume) {
885 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
887 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
891 playsink->mute_changed = TRUE;
893 GST_PLAY_SINK_UNLOCK (playsink);
897 gst_play_sink_get_mute (GstPlaySink * playsink)
900 GstPlayAudioChain *chain;
902 GST_PLAY_SINK_LOCK (playsink);
903 chain = (GstPlayAudioChain *) playsink->audiochain;
904 if (chain && chain->mute) {
905 g_object_get (chain->mute, "mute", &result, NULL);
906 playsink->mute = result;
908 result = playsink->mute;
910 GST_PLAY_SINK_UNLOCK (playsink);
916 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
920 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
921 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
925 add_chain (GstPlayChain * chain, gboolean add)
927 if (chain->added == add)
931 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
933 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
934 /* we don't want to lose our sink status */
935 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
944 activate_chain (GstPlayChain * chain, gboolean activate)
948 if (chain->activated == activate)
951 GST_OBJECT_LOCK (chain->playsink);
952 state = GST_STATE_TARGET (chain->playsink);
953 GST_OBJECT_UNLOCK (chain->playsink);
956 gst_element_set_state (chain->bin, state);
958 gst_element_set_state (chain->bin, GST_STATE_NULL);
960 chain->activated = activate;
966 element_is_sink (GstElement * element)
970 GST_OBJECT_LOCK (element);
971 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
972 GST_OBJECT_UNLOCK (element);
974 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
979 element_has_property (GstElement * element, const gchar * pname, GType type)
983 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
986 GST_DEBUG_OBJECT (element, "no %s property", pname);
990 if (type == G_TYPE_INVALID || type == pspec->value_type ||
991 g_type_is_a (pspec->value_type, type)) {
992 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
993 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
997 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
998 "and we expected it to be of type %s", pname,
999 g_type_name (pspec->value_type), g_type_name (type));
1006 const gchar *prop_name;
1009 } FindPropertyHelper;
1012 find_property (const GValue * item, FindPropertyHelper * helper)
1014 GstElement *element = g_value_get_object (item);
1015 if (helper->need_sink && !element_is_sink (element)) {
1019 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1023 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1024 (helper->need_sink) ? "sink" : "element");
1025 return 0; /* keep it */
1028 /* FIXME: why not move these functions into core? */
1029 /* find a sink in the hierarchy with a property named @name. This function does
1030 * not increase the refcount of the returned object and thus remains valid as
1031 * long as the bin is valid. */
1033 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1034 const gchar * name, GType expected_type)
1036 GstElement *result = NULL;
1039 if (element_has_property (obj, name, expected_type)) {
1041 } else if (GST_IS_BIN (obj)) {
1043 GValue item = { 0, };
1044 FindPropertyHelper helper = { name, expected_type, TRUE };
1046 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1047 found = gst_iterator_find_custom (it,
1048 (GCompareFunc) find_property, &item, &helper);
1049 gst_iterator_free (it);
1051 result = g_value_get_object (&item);
1052 /* we don't need the extra ref */
1053 g_value_unset (&item);
1059 /* find an object in the hierarchy with a property named @name */
1061 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1062 const gchar * name, GType expected_type)
1064 GstElement *result = NULL;
1067 if (GST_IS_BIN (obj)) {
1069 GValue item = { 0, };
1070 FindPropertyHelper helper = { name, expected_type, FALSE };
1072 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1073 found = gst_iterator_find_custom (it,
1074 (GCompareFunc) find_property, &item, &helper);
1075 gst_iterator_free (it);
1077 result = g_value_dup_object (&item);
1078 g_value_unset (&item);
1080 if (element_has_property (obj, name, expected_type)) {
1082 gst_object_ref (obj);
1089 do_async_start (GstPlaySink * playsink)
1091 GstMessage *message;
1093 if (!playsink->need_async_start) {
1094 GST_INFO_OBJECT (playsink, "no async_start needed");
1098 playsink->async_pending = TRUE;
1100 GST_INFO_OBJECT (playsink, "Sending async_start message");
1101 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1102 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1103 (playsink), message);
1107 do_async_done (GstPlaySink * playsink)
1109 GstMessage *message;
1111 if (playsink->async_pending) {
1112 GST_INFO_OBJECT (playsink, "Sending async_done message");
1113 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1114 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1115 (playsink), message);
1117 playsink->async_pending = FALSE;
1120 playsink->need_async_start = FALSE;
1123 /* try to change the state of an element. This function returns the element when
1124 * the state change could be performed. When this function returns NULL an error
1125 * occured and the element is unreffed if @unref is TRUE. */
1127 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1129 GstStateChangeReturn ret;
1132 ret = gst_element_set_state (element, GST_STATE_READY);
1133 if (ret == GST_STATE_CHANGE_FAILURE) {
1134 GST_DEBUG_OBJECT (playsink, "failed state change..");
1135 gst_element_set_state (element, GST_STATE_NULL);
1137 gst_object_unref (element);
1144 /* make the element (bin) that contains the elements needed to perform
1145 * video display. Only used for *raw* video streams.
1147 * +------------------------------------------------------------+
1149 * | +-------+ +----------+ +----------+ +---------+ |
1150 * | | queue | |colorspace| |videoscale| |videosink| |
1151 * | +-sink src-sink src-sink src-sink | |
1152 * | | +-------+ +----------+ +----------+ +---------+ |
1154 * +------------------------------------------------------------+
1157 static GstPlayVideoDeinterlaceChain *
1158 gen_video_deinterlace_chain (GstPlaySink * playsink)
1160 GstPlayVideoDeinterlaceChain *chain;
1163 GstElement *head = NULL, *prev = NULL;
1165 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1166 chain->chain.playsink = playsink;
1168 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1170 /* create a bin to hold objects, as we create them we add them to this bin so
1171 * that when something goes wrong we only need to unref the bin */
1172 chain->chain.bin = gst_bin_new ("vdbin");
1173 bin = GST_BIN_CAST (chain->chain.bin);
1174 gst_object_ref_sink (bin);
1176 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1177 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1178 if (chain->conv == NULL) {
1179 post_missing_element_message (playsink, COLORSPACE);
1180 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1181 (_("Missing element '%s' - check your GStreamer installation."),
1182 COLORSPACE), ("video rendering might fail"));
1184 gst_bin_add (bin, chain->conv);
1189 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1190 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1191 if (chain->deinterlace == NULL) {
1192 post_missing_element_message (playsink, "deinterlace");
1193 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1194 (_("Missing element '%s' - check your GStreamer installation."),
1195 "deinterlace"), ("deinterlacing won't work"));
1197 gst_bin_add (bin, chain->deinterlace);
1199 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1200 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1203 head = chain->deinterlace;
1205 prev = chain->deinterlace;
1209 pad = gst_element_get_static_pad (head, "sink");
1210 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1211 gst_object_unref (pad);
1213 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1217 pad = gst_element_get_static_pad (prev, "src");
1218 chain->srcpad = gst_ghost_pad_new ("src", pad);
1219 gst_object_unref (pad);
1221 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1224 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1225 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1231 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1232 (NULL), ("Failed to configure the video deinterlace chain."));
1233 free_chain ((GstPlayChain *) chain);
1238 /* make the element (bin) that contains the elements needed to perform
1241 * +------------------------------------------------------------+
1243 * | +-------+ +----------+ +----------+ +---------+ |
1244 * | | queue | |colorspace| |videoscale| |videosink| |
1245 * | +-sink src-sink src-sink src-sink | |
1246 * | | +-------+ +----------+ +----------+ +---------+ |
1248 * +------------------------------------------------------------+
1251 static GstPlayVideoChain *
1252 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1254 GstPlayVideoChain *chain;
1257 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1259 chain = g_new0 (GstPlayVideoChain, 1);
1260 chain->chain.playsink = playsink;
1261 chain->chain.raw = raw;
1263 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1265 if (playsink->video_sink) {
1266 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1267 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1269 /* only try fallback if no specific sink was chosen */
1270 if (chain->sink == NULL) {
1271 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1272 elem = gst_element_factory_make ("autovideosink", "videosink");
1273 chain->sink = try_element (playsink, elem, TRUE);
1275 if (chain->sink == NULL) {
1276 /* if default sink from config.h is different then try it too */
1277 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1278 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1279 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1280 chain->sink = try_element (playsink, elem, TRUE);
1284 playsink->video_sink = gst_object_ref (chain->sink);
1286 if (chain->sink == NULL)
1290 /* if we can disable async behaviour of the sink, we can avoid adding a
1291 * queue for the audio chain. */
1293 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1296 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1297 async, GST_ELEMENT_NAME (elem));
1298 g_object_set (elem, "async", async, NULL);
1299 chain->async = async;
1301 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1302 chain->async = TRUE;
1305 /* find ts-offset element */
1306 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1307 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1310 /* create a bin to hold objects, as we create them we add them to this bin so
1311 * that when something goes wrong we only need to unref the bin */
1312 chain->chain.bin = gst_bin_new ("vbin");
1313 bin = GST_BIN_CAST (chain->chain.bin);
1314 gst_object_ref_sink (bin);
1315 gst_bin_add (bin, chain->sink);
1317 /* decouple decoder from sink, this improves playback quite a lot since the
1318 * decoder can continue while the sink blocks for synchronisation. We don't
1319 * need a lot of buffers as this consumes a lot of memory and we don't want
1320 * too little because else we would be context switching too quickly. */
1321 chain->queue = gst_element_factory_make ("queue", "vqueue");
1322 if (chain->queue == NULL) {
1323 post_missing_element_message (playsink, "queue");
1324 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1325 (_("Missing element '%s' - check your GStreamer installation."),
1326 "queue"), ("video rendering might be suboptimal"));
1330 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1331 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1332 gst_bin_add (bin, chain->queue);
1333 head = prev = chain->queue;
1336 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1337 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1339 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1340 gst_bin_add (bin, chain->conv);
1342 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1343 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1352 GST_DEBUG_OBJECT (playsink, "linking to sink");
1353 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1354 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1358 pad = gst_element_get_static_pad (head, "sink");
1359 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1360 gst_object_unref (pad);
1362 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1369 if (!elem && !playsink->video_sink) {
1370 post_missing_element_message (playsink, "autovideosink");
1371 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1372 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1373 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1374 (_("Both autovideosink and %s elements are missing."),
1375 DEFAULT_VIDEOSINK), (NULL));
1377 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1378 (_("The autovideosink element is missing.")), (NULL));
1381 if (playsink->video_sink) {
1382 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1383 (_("Configured videosink %s is not working."),
1384 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1385 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1386 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1387 (_("Both autovideosink and %s elements are not working."),
1388 DEFAULT_VIDEOSINK), (NULL));
1390 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1391 (_("The autovideosink element is not working.")), (NULL));
1394 free_chain ((GstPlayChain *) chain);
1399 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1400 (NULL), ("Failed to configure the video sink."));
1401 /* checking sink made it READY */
1402 gst_element_set_state (chain->sink, GST_STATE_NULL);
1403 /* Remove chain from the bin to allow reuse later */
1404 gst_bin_remove (bin, chain->sink);
1405 free_chain ((GstPlayChain *) chain);
1411 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1414 GstPlayVideoChain *chain;
1415 GstStateChangeReturn ret;
1417 chain = playsink->videochain;
1419 chain->chain.raw = raw;
1421 /* if the chain was active we don't do anything */
1422 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1425 /* try to set the sink element to READY again */
1426 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1427 if (ret == GST_STATE_CHANGE_FAILURE)
1430 /* find ts-offset element */
1432 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1433 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1436 /* if we can disable async behaviour of the sink, we can avoid adding a
1437 * queue for the audio chain. */
1439 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1442 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1443 async, GST_ELEMENT_NAME (elem));
1444 g_object_set (elem, "async", async, NULL);
1445 chain->async = async;
1447 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1448 chain->async = TRUE;
1453 /* make an element for playback of video with subtitles embedded.
1454 * Only used for *raw* video streams.
1456 * +--------------------------------------------+
1458 * | +--------+ +-----------------+ |
1459 * | | queue | | subtitleoverlay | |
1460 * video--src sink---video_sink | |
1461 * | +--------+ | src--src
1462 * text------------------text_sink | |
1463 * | +-----------------+ |
1464 * +--------------------------------------------+
1467 static GstPlayTextChain *
1468 gen_text_chain (GstPlaySink * playsink)
1470 GstPlayTextChain *chain;
1473 GstPad *videosinkpad, *textsinkpad, *srcpad;
1475 chain = g_new0 (GstPlayTextChain, 1);
1476 chain->chain.playsink = playsink;
1478 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1480 chain->chain.bin = gst_bin_new ("tbin");
1481 bin = GST_BIN_CAST (chain->chain.bin);
1482 gst_object_ref_sink (bin);
1484 videosinkpad = textsinkpad = srcpad = NULL;
1486 /* first try to hook the text pad to the custom sink */
1487 if (playsink->text_sink) {
1488 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1489 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1492 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1495 /* make sure the sparse subtitles don't participate in the preroll */
1496 g_object_set (elem, "async", FALSE, NULL);
1497 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1498 gst_bin_add (bin, chain->sink);
1499 /* NOTE streamsynchronizer needs streams decoupled */
1500 /* make a little queue */
1501 chain->queue = gst_element_factory_make ("queue", "subqueue");
1502 if (chain->queue == NULL) {
1503 post_missing_element_message (playsink, "queue");
1504 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1505 (_("Missing element '%s' - check your GStreamer installation."),
1506 "queue"), ("rendering might be suboptimal"));
1508 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1509 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1510 "silent", TRUE, NULL);
1511 gst_bin_add (bin, chain->queue);
1513 /* we have a custom sink, this will be our textsinkpad */
1514 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1515 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1516 /* we're all fine now and we can add the sink to the chain */
1517 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1518 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1520 GST_WARNING_OBJECT (playsink,
1521 "can't find a sink pad on custom text sink");
1522 gst_bin_remove (bin, chain->sink);
1523 gst_bin_remove (bin, chain->queue);
1525 chain->queue = NULL;
1527 /* try to set sync to true but it's no biggie when we can't */
1529 gst_play_sink_find_property_sinks (playsink, chain->sink,
1530 "sync", G_TYPE_BOOLEAN)))
1531 g_object_set (elem, "sync", TRUE, NULL);
1534 gst_bin_remove (bin, chain->sink);
1536 GST_WARNING_OBJECT (playsink,
1537 "can't find async property in custom text sink");
1540 if (textsinkpad == NULL) {
1541 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1542 (_("Custom text sink element is not usable.")),
1543 ("fallback to default textoverlay"));
1547 if (textsinkpad == NULL) {
1548 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1549 /* make a little queue */
1550 chain->queue = gst_element_factory_make ("queue", "vqueue");
1551 if (chain->queue == NULL) {
1552 post_missing_element_message (playsink, "queue");
1553 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1554 (_("Missing element '%s' - check your GStreamer installation."),
1555 "queue"), ("video rendering might be suboptimal"));
1557 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1558 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1559 "silent", TRUE, NULL);
1560 gst_bin_add (bin, chain->queue);
1561 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1565 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1566 if (chain->overlay == NULL) {
1567 post_missing_element_message (playsink, "subtitleoverlay");
1568 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1569 (_("Missing element '%s' - check your GStreamer installation."),
1570 "subtitleoverlay"), ("subtitle rendering disabled"));
1572 GstElement *element;
1574 gst_bin_add (bin, chain->overlay);
1576 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1577 if (playsink->font_desc) {
1578 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1579 playsink->font_desc, NULL);
1581 if (playsink->subtitle_encoding) {
1582 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1583 playsink->subtitle_encoding, NULL);
1586 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1587 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1589 /* make another little queue to decouple streams */
1590 element = gst_element_factory_make ("queue", "subqueue");
1591 if (element == NULL) {
1592 post_missing_element_message (playsink, "queue");
1593 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1594 (_("Missing element '%s' - check your GStreamer installation."),
1595 "queue"), ("rendering might be suboptimal"));
1597 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1598 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1599 "silent", TRUE, NULL);
1600 gst_bin_add (bin, element);
1601 gst_element_link_pads_full (element, "src", chain->overlay,
1602 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1603 textsinkpad = gst_element_get_static_pad (element, "sink");
1604 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1610 if (videosinkpad == NULL) {
1611 /* if we still don't have a videosink, we don't have an overlay. the only
1612 * thing we can do is insert an identity and ghost the src
1614 chain->identity = gst_element_factory_make ("identity", "tidentity");
1615 if (chain->identity == NULL) {
1616 post_missing_element_message (playsink, "identity");
1617 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1618 (_("Missing element '%s' - check your GStreamer installation."),
1619 "identity"), (NULL));
1621 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1622 g_object_set (chain->identity, "silent", TRUE, NULL);
1623 gst_bin_add (bin, chain->identity);
1624 srcpad = gst_element_get_static_pad (chain->identity, "src");
1625 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1629 /* expose the ghostpads */
1631 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1632 gst_object_unref (videosinkpad);
1633 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1636 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1637 gst_object_unref (textsinkpad);
1638 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1641 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1642 gst_object_unref (srcpad);
1643 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1650 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1654 g_object_get (object, "volume", &vol, NULL);
1655 playsink->volume = vol;
1657 g_object_notify (G_OBJECT (playsink), "volume");
1661 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1665 g_object_get (object, "mute", &mute, NULL);
1666 playsink->mute = mute;
1668 g_object_notify (G_OBJECT (playsink), "mute");
1671 /* make the chain that contains the elements needed to perform
1674 * We add a tee as the first element so that we can link the visualisation chain
1675 * to it when requested.
1677 * +-------------------------------------------------------------+
1679 * | +---------+ +----------+ +---------+ +---------+ |
1680 * | |audioconv| |audioscale| | volume | |audiosink| |
1681 * | +-srck src-sink src-sink src-sink | |
1682 * | | +---------+ +----------+ +---------+ +---------+ |
1684 * +-------------------------------------------------------------+
1686 static GstPlayAudioChain *
1687 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1689 GstPlayAudioChain *chain;
1691 gboolean have_volume;
1693 GstElement *head, *prev, *elem = NULL;
1695 chain = g_new0 (GstPlayAudioChain, 1);
1696 chain->chain.playsink = playsink;
1697 chain->chain.raw = raw;
1699 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1701 if (playsink->audio_sink) {
1702 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1703 playsink->audio_sink);
1704 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1706 /* only try fallback if no specific sink was chosen */
1707 if (chain->sink == NULL) {
1708 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1709 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1710 chain->sink = try_element (playsink, elem, TRUE);
1712 if (chain->sink == NULL) {
1713 /* if default sink from config.h is different then try it too */
1714 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1715 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1716 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1717 chain->sink = try_element (playsink, elem, TRUE);
1721 playsink->audio_sink = gst_object_ref (chain->sink);
1723 if (chain->sink == NULL)
1726 chain->chain.bin = gst_bin_new ("abin");
1727 bin = GST_BIN_CAST (chain->chain.bin);
1728 gst_object_ref_sink (bin);
1729 gst_bin_add (bin, chain->sink);
1731 /* we have to add a queue when we need to decouple for the video sink in
1732 * visualisations and for streamsynchronizer */
1733 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1734 chain->queue = gst_element_factory_make ("queue", "aqueue");
1735 if (chain->queue == NULL) {
1736 post_missing_element_message (playsink, "queue");
1737 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1738 (_("Missing element '%s' - check your GStreamer installation."),
1739 "queue"), ("audio playback and visualizations might not work"));
1743 g_object_set (chain->queue, "silent", TRUE, NULL);
1744 gst_bin_add (bin, chain->queue);
1745 prev = head = chain->queue;
1748 /* find ts-offset element */
1749 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1750 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1753 /* check if the sink, or something within the sink, has the volume property.
1754 * If it does we don't need to add a volume element. */
1756 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1759 chain->volume = elem;
1761 g_signal_connect (chain->volume, "notify::volume",
1762 G_CALLBACK (notify_volume_cb), playsink);
1764 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1766 chain->sink_volume = TRUE;
1767 /* if the sink also has a mute property we can use this as well. We'll only
1768 * use the mute property if there is a volume property. We can simulate the
1769 * mute with the volume otherwise. */
1771 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1774 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1775 g_signal_connect (chain->mute, "notify::mute",
1776 G_CALLBACK (notify_mute_cb), playsink);
1778 /* use the sink to control the volume and mute */
1779 if (playsink->volume_changed) {
1780 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1781 playsink->volume_changed = FALSE;
1783 if (playsink->mute_changed) {
1785 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1788 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1790 playsink->mute_changed = FALSE;
1793 /* no volume, we need to add a volume element when we can */
1794 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1795 have_volume = FALSE;
1796 chain->sink_volume = FALSE;
1799 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1800 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1801 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1802 gboolean use_volume =
1803 !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME;
1804 GST_DEBUG_OBJECT (playsink,
1805 "creating audioconvert with use-converters %d, use-volume %d",
1806 use_converters, use_volume);
1808 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1809 "use-converters", use_converters, "use-volume", use_volume, NULL);
1810 gst_bin_add (bin, chain->conv);
1812 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1813 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1820 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1821 GstPlaySinkAudioConvert *conv =
1822 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1825 chain->volume = conv->volume;
1828 g_signal_connect (chain->volume, "notify::volume",
1829 G_CALLBACK (notify_volume_cb), playsink);
1831 /* volume also has the mute property */
1832 chain->mute = chain->volume;
1833 g_signal_connect (chain->mute, "notify::mute",
1834 G_CALLBACK (notify_mute_cb), playsink);
1836 /* configure with the latest volume and mute */
1837 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1839 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1845 /* we only have to link to the previous element if we have something in
1846 * front of the sink */
1847 GST_DEBUG_OBJECT (playsink, "linking to sink");
1848 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1849 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1853 /* post a warning if we have no way to configure the volume */
1855 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1856 (_("No volume control found")), ("Volume/mute is not available"));
1859 /* and ghost the sinkpad of the headmost element */
1860 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1861 pad = gst_element_get_static_pad (head, "sink");
1862 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1863 gst_object_unref (pad);
1864 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1871 if (!elem && !playsink->audio_sink) {
1872 post_missing_element_message (playsink, "autoaudiosink");
1873 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1874 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1875 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1876 (_("Both autoaudiosink and %s elements are missing."),
1877 DEFAULT_AUDIOSINK), (NULL));
1879 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1880 (_("The autoaudiosink element is missing.")), (NULL));
1883 if (playsink->audio_sink) {
1884 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1885 (_("Configured audiosink %s is not working."),
1886 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1887 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1888 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1889 (_("Both autoaudiosink and %s elements are not working."),
1890 DEFAULT_AUDIOSINK), (NULL));
1892 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1893 (_("The autoaudiosink element is not working.")), (NULL));
1896 free_chain ((GstPlayChain *) chain);
1901 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1902 (NULL), ("Failed to configure the audio sink."));
1903 /* checking sink made it READY */
1904 gst_element_set_state (chain->sink, GST_STATE_NULL);
1905 /* Remove chain from the bin to allow reuse later */
1906 gst_bin_remove (bin, chain->sink);
1907 free_chain ((GstPlayChain *) chain);
1913 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1916 GstPlayAudioChain *chain;
1917 GstStateChangeReturn ret;
1919 chain = playsink->audiochain;
1921 chain->chain.raw = raw;
1923 /* if the chain was active we don't do anything */
1924 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1927 /* try to set the sink element to READY again */
1928 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1929 if (ret == GST_STATE_CHANGE_FAILURE)
1932 /* find ts-offset element */
1933 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1934 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1937 /* check if the sink, or something within the sink, has the volume property.
1938 * If it does we don't need to add a volume element. */
1940 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1943 chain->volume = elem;
1945 if (playsink->volume_changed) {
1946 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1948 /* use the sink to control the volume */
1949 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1950 playsink->volume_changed = FALSE;
1953 g_signal_connect (chain->volume, "notify::volume",
1954 G_CALLBACK (notify_volume_cb), playsink);
1955 /* if the sink also has a mute property we can use this as well. We'll only
1956 * use the mute property if there is a volume property. We can simulate the
1957 * mute with the volume otherwise. */
1959 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1962 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1963 g_signal_connect (chain->mute, "notify::mute",
1964 G_CALLBACK (notify_mute_cb), playsink);
1967 g_object_set (chain->conv, "use-volume", FALSE, NULL);
1969 GstPlaySinkAudioConvert *conv =
1970 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1972 /* no volume, we need to add a volume element when we can */
1973 g_object_set (chain->conv, "use-volume", TRUE, NULL);
1974 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1976 /* Disconnect signals */
1977 disconnect_chain (chain, playsink);
1980 chain->volume = conv->volume;
1981 chain->mute = chain->volume;
1983 g_signal_connect (chain->volume, "notify::volume",
1984 G_CALLBACK (notify_volume_cb), playsink);
1986 g_signal_connect (chain->mute, "notify::mute",
1987 G_CALLBACK (notify_mute_cb), playsink);
1989 /* configure with the latest volume and mute */
1990 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1991 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1994 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2000 * +-------------------------------------------------------------------+
2002 * | +----------+ +------------+ +----------+ +-------+ |
2003 * | | visqueue | | audioconv | | audiores | | vis | |
2004 * | +-sink src-sink + samp src-sink src-sink src-+ |
2005 * | | +----------+ +------------+ +----------+ +-------+ | |
2007 * +-------------------------------------------------------------------+
2010 static GstPlayVisChain *
2011 gen_vis_chain (GstPlaySink * playsink)
2013 GstPlayVisChain *chain;
2019 chain = g_new0 (GstPlayVisChain, 1);
2020 chain->chain.playsink = playsink;
2022 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2024 chain->chain.bin = gst_bin_new ("visbin");
2025 bin = GST_BIN_CAST (chain->chain.bin);
2026 gst_object_ref_sink (bin);
2028 /* we're queuing raw audio here, we can remove this queue when we can disable
2029 * async behaviour in the video sink. */
2030 chain->queue = gst_element_factory_make ("queue", "visqueue");
2031 if (chain->queue == NULL)
2033 g_object_set (chain->queue, "silent", TRUE, NULL);
2034 gst_bin_add (bin, chain->queue);
2036 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2037 if (chain->conv == NULL)
2038 goto no_audioconvert;
2039 gst_bin_add (bin, chain->conv);
2041 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2042 if (chain->resample == NULL)
2043 goto no_audioresample;
2044 gst_bin_add (bin, chain->resample);
2046 /* this pad will be used for blocking the dataflow and switching the vis
2048 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2050 if (playsink->visualisation) {
2051 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2052 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2054 if (chain->vis == NULL) {
2055 GST_DEBUG_OBJECT (playsink, "trying goom");
2056 elem = gst_element_factory_make ("goom", "vis");
2057 chain->vis = try_element (playsink, elem, TRUE);
2059 if (chain->vis == NULL)
2062 gst_bin_add (bin, chain->vis);
2064 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2065 GST_PAD_LINK_CHECK_NOTHING);
2067 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2068 GST_PAD_LINK_CHECK_NOTHING);
2070 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2071 GST_PAD_LINK_CHECK_NOTHING);
2075 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2076 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2078 pad = gst_element_get_static_pad (chain->queue, "sink");
2079 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2080 gst_object_unref (pad);
2081 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2083 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2084 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2091 post_missing_element_message (playsink, "queue");
2092 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2093 (_("Missing element '%s' - check your GStreamer installation."),
2095 free_chain ((GstPlayChain *) chain);
2100 post_missing_element_message (playsink, "audioconvert");
2101 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2102 (_("Missing element '%s' - check your GStreamer installation."),
2103 "audioconvert"), ("possibly a liboil version mismatch?"));
2104 free_chain ((GstPlayChain *) chain);
2109 post_missing_element_message (playsink, "audioresample");
2110 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2111 (_("Missing element '%s' - check your GStreamer installation."),
2112 "audioresample"), (NULL));
2113 free_chain ((GstPlayChain *) chain);
2118 post_missing_element_message (playsink, "goom");
2119 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2120 (_("Missing element '%s' - check your GStreamer installation."),
2122 free_chain ((GstPlayChain *) chain);
2127 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2128 (NULL), ("Failed to configure the visualisation element."));
2129 /* element made it to READY */
2130 gst_element_set_state (chain->vis, GST_STATE_NULL);
2131 free_chain ((GstPlayChain *) chain);
2136 /* this function is called when all the request pads are requested and when we
2137 * have to construct the final pipeline. Based on the flags we construct the
2138 * final output pipelines.
2141 gst_play_sink_reconfigure (GstPlaySink * playsink)
2144 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2146 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2148 /* assume we need nothing */
2149 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2151 GST_PLAY_SINK_LOCK (playsink);
2152 GST_OBJECT_LOCK (playsink);
2153 /* get flags, there are protected with the object lock */
2154 flags = playsink->flags;
2155 GST_OBJECT_UNLOCK (playsink);
2157 /* figure out which components we need */
2158 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2159 /* we have subtitles and we are requested to show it */
2163 if (((flags & GST_PLAY_FLAG_VIDEO)
2164 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2165 /* we have video and we are requested to show it */
2168 /* we only deinterlace if native video is not requested and
2169 * we have raw video */
2170 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2171 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2172 need_deinterlace = TRUE;
2175 if (playsink->audio_pad) {
2176 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2179 if (playsink->audio_pad_raw) {
2180 /* only can do vis with raw uncompressed audio */
2181 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2182 /* also add video when we add visualisation */
2189 /* we have a text_pad and we need text rendering, in this case we need a
2190 * video_pad to combine the video with the text or visualizations */
2191 if (need_text && !need_video) {
2192 if (playsink->video_pad) {
2194 } else if (need_audio) {
2195 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2196 (_("Can't play a text file without video or visualizations.")),
2197 ("Have text pad but no video pad or visualizations"));
2200 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2201 (_("Can't play a text file without video or visualizations.")),
2202 ("Have text pad but no video pad or visualizations"));
2203 GST_PLAY_SINK_UNLOCK (playsink);
2208 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2209 need_video, need_vis, need_text);
2211 /* set up video pipeline */
2213 gboolean raw, async;
2215 /* we need a raw sink when we do vis or when we have a raw pad */
2216 raw = need_vis ? TRUE : playsink->video_pad_raw;
2217 /* we try to set the sink async=FALSE when we need vis, this way we can
2218 * avoid a queue in the audio chain. */
2221 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2222 playsink->video_pad_raw);
2224 if (playsink->videochain) {
2225 /* try to reactivate the chain */
2226 if (!setup_video_chain (playsink, raw, async)) {
2227 if (playsink->video_sinkpad_stream_synchronizer) {
2228 gst_element_release_request_pad (GST_ELEMENT_CAST
2229 (playsink->stream_synchronizer),
2230 playsink->video_sinkpad_stream_synchronizer);
2231 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2232 playsink->video_sinkpad_stream_synchronizer = NULL;
2233 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2234 playsink->video_srcpad_stream_synchronizer = NULL;
2237 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2239 /* Remove the sink from the bin to keep its state
2240 * and unparent it to allow reuse */
2241 if (playsink->videochain->sink)
2242 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2243 playsink->videochain->sink);
2245 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2246 free_chain ((GstPlayChain *) playsink->videochain);
2247 playsink->videochain = NULL;
2251 if (!playsink->videochain)
2252 playsink->videochain = gen_video_chain (playsink, raw, async);
2253 if (!playsink->videochain)
2256 if (!playsink->video_sinkpad_stream_synchronizer) {
2257 GValue item = { 0, };
2260 playsink->video_sinkpad_stream_synchronizer =
2261 gst_element_get_request_pad (GST_ELEMENT_CAST
2262 (playsink->stream_synchronizer), "sink_%u");
2263 it = gst_pad_iterate_internal_links
2264 (playsink->video_sinkpad_stream_synchronizer);
2266 gst_iterator_next (it, &item);
2267 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2268 g_value_unset (&item);
2269 g_assert (playsink->video_srcpad_stream_synchronizer);
2270 gst_iterator_free (it);
2273 if (playsink->video_pad)
2274 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2275 playsink->video_sinkpad_stream_synchronizer);
2277 if (need_deinterlace) {
2278 if (!playsink->videodeinterlacechain)
2279 playsink->videodeinterlacechain =
2280 gen_video_deinterlace_chain (playsink);
2281 if (!playsink->videodeinterlacechain)
2284 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2286 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2288 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2289 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2291 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2292 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2294 if (playsink->videodeinterlacechain) {
2295 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2296 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2301 GST_DEBUG_OBJECT (playsink, "adding video chain");
2302 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2303 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2304 /* if we are not part of vis or subtitles, set the ghostpad target */
2305 if (!need_vis && !need_text && (!playsink->textchain
2306 || !playsink->text_pad)) {
2307 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2308 if (need_deinterlace)
2309 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2310 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2312 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2313 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2316 GST_DEBUG_OBJECT (playsink, "no video needed");
2317 if (playsink->videochain) {
2318 GST_DEBUG_OBJECT (playsink, "removing video chain");
2319 if (playsink->vischain) {
2322 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2324 /* also had visualisation, release the tee srcpad before we then
2325 * unlink the video from it */
2326 if (playsink->audio_tee_vissrc) {
2327 gst_element_release_request_pad (playsink->audio_tee,
2328 playsink->audio_tee_vissrc);
2329 gst_object_unref (playsink->audio_tee_vissrc);
2330 playsink->audio_tee_vissrc = NULL;
2333 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2334 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2337 if (playsink->video_sinkpad_stream_synchronizer) {
2338 gst_element_release_request_pad (GST_ELEMENT_CAST
2339 (playsink->stream_synchronizer),
2340 playsink->video_sinkpad_stream_synchronizer);
2341 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2342 playsink->video_sinkpad_stream_synchronizer = NULL;
2343 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2344 playsink->video_srcpad_stream_synchronizer = NULL;
2347 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2348 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2349 if (playsink->videochain->ts_offset)
2350 gst_object_unref (playsink->videochain->ts_offset);
2351 playsink->videochain->ts_offset = NULL;
2354 if (playsink->videodeinterlacechain) {
2355 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2356 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2359 if (playsink->video_pad)
2360 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2366 GST_DEBUG_OBJECT (playsink, "adding audio");
2368 /* get a raw sink if we are asked for a raw pad */
2369 raw = playsink->audio_pad_raw;
2371 if (playsink->audiochain) {
2372 /* try to reactivate the chain */
2373 if (!setup_audio_chain (playsink, raw)) {
2374 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2375 if (playsink->audio_tee_asrc) {
2376 gst_element_release_request_pad (playsink->audio_tee,
2377 playsink->audio_tee_asrc);
2378 gst_object_unref (playsink->audio_tee_asrc);
2379 playsink->audio_tee_asrc = NULL;
2382 if (playsink->audio_sinkpad_stream_synchronizer) {
2383 gst_element_release_request_pad (GST_ELEMENT_CAST
2384 (playsink->stream_synchronizer),
2385 playsink->audio_sinkpad_stream_synchronizer);
2386 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2387 playsink->audio_sinkpad_stream_synchronizer = NULL;
2388 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2389 playsink->audio_srcpad_stream_synchronizer = NULL;
2392 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2394 /* Remove the sink from the bin to keep its state
2395 * and unparent it to allow reuse */
2396 if (playsink->audiochain->sink)
2397 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2398 playsink->audiochain->sink);
2400 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2401 disconnect_chain (playsink->audiochain, playsink);
2402 playsink->audiochain->volume = NULL;
2403 playsink->audiochain->mute = NULL;
2404 if (playsink->audiochain->ts_offset)
2405 gst_object_unref (playsink->audiochain->ts_offset);
2406 playsink->audiochain->ts_offset = NULL;
2407 free_chain ((GstPlayChain *) playsink->audiochain);
2408 playsink->audiochain = NULL;
2409 playsink->volume_changed = playsink->mute_changed = FALSE;
2413 if (!playsink->audiochain) {
2414 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2415 playsink->audiochain = gen_audio_chain (playsink, raw);
2418 if (!playsink->audio_sinkpad_stream_synchronizer) {
2419 GValue item = { 0, };
2422 playsink->audio_sinkpad_stream_synchronizer =
2423 gst_element_get_request_pad (GST_ELEMENT_CAST
2424 (playsink->stream_synchronizer), "sink_%u");
2425 it = gst_pad_iterate_internal_links
2426 (playsink->audio_sinkpad_stream_synchronizer);
2428 gst_iterator_next (it, &item);
2429 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
2430 g_value_unset (&item);
2431 g_assert (playsink->audio_srcpad_stream_synchronizer);
2432 gst_iterator_free (it);
2435 if (playsink->audiochain) {
2436 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2437 if (playsink->audio_tee_asrc == NULL) {
2438 playsink->audio_tee_asrc =
2439 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2441 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2442 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2443 gst_pad_link_full (playsink->audio_tee_asrc,
2444 playsink->audio_sinkpad_stream_synchronizer,
2445 GST_PAD_LINK_CHECK_NOTHING);
2446 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2447 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2450 GST_DEBUG_OBJECT (playsink, "no audio needed");
2451 /* we have no audio or we are requested to not play audio */
2452 if (playsink->audiochain) {
2453 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2454 /* release the audio pad */
2455 if (playsink->audio_tee_asrc) {
2456 gst_element_release_request_pad (playsink->audio_tee,
2457 playsink->audio_tee_asrc);
2458 gst_object_unref (playsink->audio_tee_asrc);
2459 playsink->audio_tee_asrc = NULL;
2462 if (playsink->audio_sinkpad_stream_synchronizer) {
2463 gst_element_release_request_pad (GST_ELEMENT_CAST
2464 (playsink->stream_synchronizer),
2465 playsink->audio_sinkpad_stream_synchronizer);
2466 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2467 playsink->audio_sinkpad_stream_synchronizer = NULL;
2468 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2469 playsink->audio_srcpad_stream_synchronizer = NULL;
2472 if (playsink->audiochain->sink_volume) {
2473 disconnect_chain (playsink->audiochain, playsink);
2474 playsink->audiochain->volume = NULL;
2475 playsink->audiochain->mute = NULL;
2476 if (playsink->audiochain->ts_offset)
2477 gst_object_unref (playsink->audiochain->ts_offset);
2478 playsink->audiochain->ts_offset = NULL;
2480 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2481 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2488 if (!playsink->vischain)
2489 playsink->vischain = gen_vis_chain (playsink);
2491 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2493 if (playsink->vischain) {
2494 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2496 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2497 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2498 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2499 if (playsink->audio_tee_vissrc == NULL) {
2500 playsink->audio_tee_vissrc =
2501 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2503 gst_pad_link_full (playsink->audio_tee_vissrc,
2504 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2505 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2506 GST_PAD_LINK_CHECK_NOTHING);
2507 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2508 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2509 gst_object_unref (srcpad);
2512 GST_DEBUG_OBJECT (playsink, "no vis needed");
2513 if (playsink->vischain) {
2514 if (playsink->audio_tee_vissrc) {
2515 gst_element_release_request_pad (playsink->audio_tee,
2516 playsink->audio_tee_vissrc);
2517 gst_object_unref (playsink->audio_tee_vissrc);
2518 playsink->audio_tee_vissrc = NULL;
2520 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2521 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2522 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2527 GST_DEBUG_OBJECT (playsink, "adding text");
2528 if (!playsink->textchain) {
2529 GST_DEBUG_OBJECT (playsink, "creating text chain");
2530 playsink->textchain = gen_text_chain (playsink);
2532 if (playsink->textchain) {
2535 GST_DEBUG_OBJECT (playsink, "adding text chain");
2536 if (playsink->textchain->overlay)
2537 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2539 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2541 if (!playsink->text_sinkpad_stream_synchronizer) {
2542 GValue item = { 0, };
2544 playsink->text_sinkpad_stream_synchronizer =
2545 gst_element_get_request_pad (GST_ELEMENT_CAST
2546 (playsink->stream_synchronizer), "sink_%u");
2547 it = gst_pad_iterate_internal_links
2548 (playsink->text_sinkpad_stream_synchronizer);
2550 gst_iterator_next (it, &item);
2551 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
2552 g_value_unset (&item);
2553 g_assert (playsink->text_srcpad_stream_synchronizer);
2554 gst_iterator_free (it);
2556 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2557 playsink->text_sinkpad_stream_synchronizer);
2558 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2559 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2566 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2567 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2568 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2569 GST_PAD_LINK_CHECK_NOTHING);
2570 gst_object_unref (srcpad);
2572 if (need_deinterlace)
2573 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2574 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2576 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2577 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2579 gst_pad_link_full (playsink->textchain->srcpad,
2580 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2582 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2585 GST_DEBUG_OBJECT (playsink, "no text needed");
2586 /* we have no subtitles/text or we are requested to not show them */
2588 if (playsink->text_sinkpad_stream_synchronizer) {
2589 gst_element_release_request_pad (GST_ELEMENT_CAST
2590 (playsink->stream_synchronizer),
2591 playsink->text_sinkpad_stream_synchronizer);
2592 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2593 playsink->text_sinkpad_stream_synchronizer = NULL;
2594 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2595 playsink->text_srcpad_stream_synchronizer = NULL;
2598 if (playsink->textchain) {
2599 if (playsink->text_pad == NULL) {
2600 /* no text pad, remove the chain entirely */
2601 GST_DEBUG_OBJECT (playsink, "removing text chain");
2602 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2603 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2605 /* we have a chain and a textpad, turn the subtitles off */
2606 GST_DEBUG_OBJECT (playsink, "turning off the text");
2607 if (playsink->textchain->overlay)
2608 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2612 if (!need_video && playsink->video_pad) {
2613 if (playsink->video_sinkpad_stream_synchronizer) {
2614 gst_element_release_request_pad (GST_ELEMENT_CAST
2615 (playsink->stream_synchronizer),
2616 playsink->video_sinkpad_stream_synchronizer);
2617 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2618 playsink->video_sinkpad_stream_synchronizer = NULL;
2619 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2620 playsink->video_srcpad_stream_synchronizer = NULL;
2623 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2626 if (playsink->text_pad && !playsink->textchain)
2627 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2629 update_av_offset (playsink);
2630 do_async_done (playsink);
2631 GST_PLAY_SINK_UNLOCK (playsink);
2638 /* gen_ chain already posted error */
2639 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2640 GST_PLAY_SINK_UNLOCK (playsink);
2646 * gst_play_sink_set_flags:
2647 * @playsink: a #GstPlaySink
2648 * @flags: #GstPlayFlags
2650 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2651 * when constructing the sink pipelins.
2653 * Returns: TRUE if the flags could be configured.
2656 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2658 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2660 GST_OBJECT_LOCK (playsink);
2661 playsink->flags = flags;
2662 GST_OBJECT_UNLOCK (playsink);
2668 * gst_play_sink_get_flags:
2669 * @playsink: a #GstPlaySink
2671 * Get the flags of @playsink. That flags control the behaviour of the sink when
2672 * it constructs the sink pipelines.
2674 * Returns: the currently configured #GstPlayFlags.
2677 gst_play_sink_get_flags (GstPlaySink * playsink)
2681 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2683 GST_OBJECT_LOCK (playsink);
2684 res = playsink->flags;
2685 GST_OBJECT_UNLOCK (playsink);
2691 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2693 GstPlayTextChain *chain;
2695 GST_PLAY_SINK_LOCK (playsink);
2696 chain = (GstPlayTextChain *) playsink->textchain;
2697 g_free (playsink->font_desc);
2698 playsink->font_desc = g_strdup (desc);
2699 if (chain && chain->overlay) {
2700 g_object_set (chain->overlay, "font-desc", desc, NULL);
2702 GST_PLAY_SINK_UNLOCK (playsink);
2706 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2708 gchar *result = NULL;
2709 GstPlayTextChain *chain;
2711 GST_PLAY_SINK_LOCK (playsink);
2712 chain = (GstPlayTextChain *) playsink->textchain;
2713 if (chain && chain->overlay) {
2714 g_object_get (chain->overlay, "font-desc", &result, NULL);
2715 playsink->font_desc = g_strdup (result);
2717 result = g_strdup (playsink->font_desc);
2719 GST_PLAY_SINK_UNLOCK (playsink);
2725 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2726 const gchar * encoding)
2728 GstPlayTextChain *chain;
2730 GST_PLAY_SINK_LOCK (playsink);
2731 chain = (GstPlayTextChain *) playsink->textchain;
2732 g_free (playsink->subtitle_encoding);
2733 playsink->subtitle_encoding = g_strdup (encoding);
2734 if (chain && chain->overlay) {
2735 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2737 GST_PLAY_SINK_UNLOCK (playsink);
2741 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2743 gchar *result = NULL;
2744 GstPlayTextChain *chain;
2746 GST_PLAY_SINK_LOCK (playsink);
2747 chain = (GstPlayTextChain *) playsink->textchain;
2748 if (chain && chain->overlay) {
2749 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2750 playsink->subtitle_encoding = g_strdup (result);
2752 result = g_strdup (playsink->subtitle_encoding);
2754 GST_PLAY_SINK_UNLOCK (playsink);
2760 update_av_offset (GstPlaySink * playsink)
2763 GstPlayAudioChain *achain;
2764 GstPlayVideoChain *vchain;
2766 av_offset = playsink->av_offset;
2767 achain = (GstPlayAudioChain *) playsink->audiochain;
2768 vchain = (GstPlayVideoChain *) playsink->videochain;
2770 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2771 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2772 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2774 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2779 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2781 GST_PLAY_SINK_LOCK (playsink);
2782 playsink->av_offset = av_offset;
2783 update_av_offset (playsink);
2784 GST_PLAY_SINK_UNLOCK (playsink);
2788 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2792 GST_PLAY_SINK_LOCK (playsink);
2793 result = playsink->av_offset;
2794 GST_PLAY_SINK_UNLOCK (playsink);
2800 * gst_play_sink_get_last_sample:
2801 * @playsink: a #GstPlaySink
2803 * Get the last displayed sample from @playsink. This sample is in the native
2804 * format of the sink element, the caps in the result sample contain the format
2805 * of the frame data.
2807 * Returns: a #GstSample with the frame data or %NULL when no video frame is
2811 gst_play_sink_get_last_sample (GstPlaySink * playsink)
2813 GstSample *result = NULL;
2814 GstPlayVideoChain *chain;
2816 GST_PLAY_SINK_LOCK (playsink);
2817 GST_DEBUG_OBJECT (playsink, "taking last sample");
2818 /* get the video chain if we can */
2819 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2820 GST_DEBUG_OBJECT (playsink, "found video chain");
2821 /* see if the chain is active */
2822 if (chain->chain.activated && chain->sink) {
2825 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2827 /* find and get the last-buffer property now */
2829 gst_play_sink_find_property (playsink, chain->sink,
2830 "last-sample", GST_TYPE_SAMPLE))) {
2831 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
2832 g_object_get (elem, "last-sample", &result, NULL);
2833 gst_object_unref (elem);
2837 GST_PLAY_SINK_UNLOCK (playsink);
2843 * gst_play_sink_convert_sample:
2844 * @playsink: a #GstPlaySink
2847 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2848 * be in the native format of the sink element and the caps on the buffer
2849 * describe the format of the frame. If @caps is not %NULL, the video
2850 * frame will be converted to the format of the caps.
2852 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2853 * available or when the conversion failed.
2856 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
2861 result = gst_play_sink_get_last_sample (playsink);
2862 if (result != NULL && caps != NULL) {
2865 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
2866 if (temp == NULL && err)
2869 gst_sample_unref (result);
2877 /* I'm really uncertain whether we should make playsink post an error
2878 * on the bus or not. It's not like it's a critical issue regarding
2879 * playsink behaviour. */
2880 GST_ERROR ("Error converting frame: %s", err->message);
2881 gst_sample_unref (result);
2888 is_raw_structure (GstStructure * s)
2892 name = gst_structure_get_name (s);
2894 if (g_str_has_prefix (name, "video/x-raw") ||
2895 g_str_has_prefix (name, "audio/x-raw"))
2901 is_raw_pad (GstPad * pad)
2903 GstPad *peer = gst_pad_get_peer (pad);
2905 gboolean raw = TRUE;
2910 caps = gst_pad_get_current_caps (peer);
2914 caps = gst_pad_query_caps (peer, NULL);
2916 n = gst_caps_get_size (caps);
2917 for (i = 0; i < n; i++) {
2918 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2922 } else if (raw != r) {
2923 GST_ERROR_OBJECT (pad,
2924 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2930 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2932 gst_caps_unref (caps);
2933 gst_object_unref (peer);
2938 static GstPadProbeReturn
2939 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
2940 gpointer user_data);
2943 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
2945 if (playsink->video_pad) {
2947 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2948 (playsink->video_pad)));
2949 if (blocked && playsink->video_block_id == 0) {
2950 playsink->video_block_id =
2951 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
2952 sinkpad_blocked_cb, gst_object_ref (playsink),
2953 (GDestroyNotify) gst_object_unref);
2954 } else if (!blocked && playsink->video_block_id) {
2955 gst_pad_remove_probe (opad, playsink->video_block_id);
2956 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2957 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2958 playsink->video_block_id = 0;
2959 playsink->video_pad_blocked = FALSE;
2961 gst_object_unref (opad);
2966 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
2968 if (playsink->audio_pad) {
2970 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2971 (playsink->audio_pad)));
2972 if (blocked && playsink->audio_block_id == 0) {
2973 playsink->audio_block_id =
2974 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
2975 sinkpad_blocked_cb, gst_object_ref (playsink),
2976 (GDestroyNotify) gst_object_unref);
2977 } else if (!blocked && playsink->audio_block_id) {
2978 gst_pad_remove_probe (opad, playsink->audio_block_id);
2979 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2980 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2981 playsink->audio_block_id = 0;
2982 playsink->audio_pad_blocked = FALSE;
2984 gst_object_unref (opad);
2989 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
2991 if (playsink->text_pad) {
2993 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2994 (playsink->text_pad)));
2995 if (blocked && playsink->text_block_id == 0) {
2996 playsink->text_block_id =
2997 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
2998 sinkpad_blocked_cb, gst_object_ref (playsink),
2999 (GDestroyNotify) gst_object_unref);
3000 } else if (!blocked && playsink->text_block_id) {
3001 gst_pad_remove_probe (opad, playsink->text_block_id);
3002 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3003 playsink->text_block_id = 0;
3004 playsink->text_pad_blocked = FALSE;
3006 gst_object_unref (opad);
3010 static GstPadProbeReturn
3011 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3014 GstPlaySink *playsink = (GstPlaySink *) user_data;
3017 GST_PLAY_SINK_LOCK (playsink);
3019 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3020 if (pad == playsink->video_pad) {
3021 playsink->video_pad_blocked = TRUE;
3022 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3023 } else if (pad == playsink->audio_pad) {
3024 playsink->audio_pad_blocked = TRUE;
3025 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3026 } else if (pad == playsink->text_pad) {
3027 playsink->text_pad_blocked = TRUE;
3028 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3031 /* We reconfigure when for ALL streams:
3032 * * there isn't a pad
3033 * * OR the pad is blocked
3034 * * OR there are no pending blocks on that pad
3037 if ((!playsink->video_pad || playsink->video_pad_blocked
3038 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3039 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3040 && (!playsink->text_pad || playsink->text_pad_blocked
3041 || !PENDING_TEXT_BLOCK (playsink))) {
3042 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3044 if (playsink->video_pad) {
3045 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3046 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3047 playsink->video_pad_raw);
3050 if (playsink->audio_pad) {
3051 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3052 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3053 playsink->audio_pad_raw);
3056 gst_play_sink_reconfigure (playsink);
3058 video_set_blocked (playsink, FALSE);
3059 audio_set_blocked (playsink, FALSE);
3060 text_set_blocked (playsink, FALSE);
3063 gst_object_unref (pad);
3065 GST_PLAY_SINK_UNLOCK (playsink);
3067 return GST_PAD_PROBE_OK;
3071 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3073 gboolean reconfigure = FALSE;
3077 g_object_get (pad, "caps", &caps, NULL);
3081 if (pad == playsink->audio_pad) {
3082 raw = is_raw_pad (pad);
3083 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3084 && playsink->audiochain;
3085 GST_DEBUG_OBJECT (pad,
3086 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3088 } else if (pad == playsink->video_pad) {
3089 raw = is_raw_pad (pad);
3090 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3091 && playsink->videochain;
3092 GST_DEBUG_OBJECT (pad,
3093 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3097 gst_caps_unref (caps);
3100 GST_PLAY_SINK_LOCK (playsink);
3101 video_set_blocked (playsink, TRUE);
3102 audio_set_blocked (playsink, TRUE);
3103 text_set_blocked (playsink, TRUE);
3104 GST_PLAY_SINK_UNLOCK (playsink);
3109 * gst_play_sink_request_pad
3110 * @playsink: a #GstPlaySink
3111 * @type: a #GstPlaySinkType
3113 * Create or return a pad of @type.
3115 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3118 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3121 gboolean created = FALSE;
3122 gboolean activate = TRUE;
3123 const gchar *pad_name = NULL;
3124 gulong *block_id = NULL;
3126 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3128 GST_PLAY_SINK_LOCK (playsink);
3130 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3131 case GST_PLAY_SINK_TYPE_AUDIO:
3132 pad_name = "audio_sink";
3133 if (!playsink->audio_tee) {
3134 GST_LOG_OBJECT (playsink, "creating tee");
3135 /* create tee when needed. This element will feed the audio sink chain
3136 * and the vis chain. */
3137 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3138 if (playsink->audio_tee == NULL) {
3139 post_missing_element_message (playsink, "tee");
3140 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3141 (_("Missing element '%s' - check your GStreamer installation."),
3146 playsink->audio_tee_sink =
3147 gst_element_get_static_pad (playsink->audio_tee, "sink");
3148 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3149 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3152 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3154 if (!playsink->audio_pad) {
3155 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3156 playsink->audio_pad =
3157 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3158 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3159 G_CALLBACK (caps_notify_cb), playsink);
3162 playsink->audio_pad_raw = FALSE;
3163 res = playsink->audio_pad;
3164 block_id = &playsink->audio_block_id;
3166 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3167 case GST_PLAY_SINK_TYPE_VIDEO:
3168 pad_name = "video_sink";
3169 if (!playsink->video_pad) {
3170 GST_LOG_OBJECT (playsink, "ghosting videosink");
3171 playsink->video_pad =
3172 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3173 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3174 G_CALLBACK (caps_notify_cb), playsink);
3177 playsink->video_pad_raw = FALSE;
3178 res = playsink->video_pad;
3179 block_id = &playsink->video_block_id;
3181 case GST_PLAY_SINK_TYPE_TEXT:
3182 GST_LOG_OBJECT (playsink, "ghosting text");
3183 if (!playsink->text_pad) {
3184 playsink->text_pad =
3185 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3188 res = playsink->text_pad;
3189 block_id = &playsink->text_block_id;
3191 case GST_PLAY_SINK_TYPE_FLUSHING:
3195 /* we need a unique padname for the flushing pad. */
3196 padname = g_strdup_printf ("flushing_%u", playsink->count);
3197 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3208 GST_PLAY_SINK_UNLOCK (playsink);
3210 if (created && res) {
3211 /* we have to add the pad when it's active or we get an error when the
3212 * element is 'running' */
3213 gst_pad_set_active (res, TRUE);
3214 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3215 if (block_id && *block_id == 0) {
3217 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3220 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3221 sinkpad_blocked_cb, gst_object_ref (playsink),
3222 (GDestroyNotify) gst_object_unref);
3223 PENDING_FLAG_SET (playsink, type);
3224 gst_object_unref (blockpad);
3227 gst_pad_set_active (res, activate);
3234 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3235 const gchar * name, const GstCaps * caps)
3239 GstPlaySinkType type;
3240 const gchar *tplname;
3242 g_return_val_if_fail (templ != NULL, NULL);
3244 GST_DEBUG_OBJECT (element, "name:%s", name);
3246 psink = GST_PLAY_SINK (element);
3247 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3249 /* Figure out the GstPlaySinkType based on the template */
3250 if (!strcmp (tplname, "audio_sink"))
3251 type = GST_PLAY_SINK_TYPE_AUDIO;
3252 else if (!strcmp (tplname, "audio_raw_sink"))
3253 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3254 else if (!strcmp (tplname, "video_sink"))
3255 type = GST_PLAY_SINK_TYPE_VIDEO;
3256 else if (!strcmp (tplname, "video_raw_sink"))
3257 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3258 else if (!strcmp (tplname, "text_sink"))
3259 type = GST_PLAY_SINK_TYPE_TEXT;
3261 goto unknown_template;
3263 pad = gst_play_sink_request_pad (psink, type);
3267 GST_WARNING_OBJECT (element, "Unknown pad template");
3272 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3274 GstPad **res = NULL;
3275 gboolean untarget = TRUE;
3277 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3279 GST_PLAY_SINK_LOCK (playsink);
3280 if (pad == playsink->video_pad) {
3281 res = &playsink->video_pad;
3282 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3284 } else if (pad == playsink->audio_pad) {
3285 res = &playsink->audio_pad;
3286 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3288 } else if (pad == playsink->text_pad) {
3289 res = &playsink->text_pad;
3291 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3295 GST_PLAY_SINK_UNLOCK (playsink);
3298 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3299 gst_pad_set_active (*res, FALSE);
3301 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3302 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3304 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3305 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3311 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3313 GstPlaySink *psink = GST_PLAY_SINK (element);
3315 gst_play_sink_release_pad (psink, pad);
3319 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3321 GstPlaySink *playsink;
3323 playsink = GST_PLAY_SINK_CAST (bin);
3325 switch (GST_MESSAGE_TYPE (message)) {
3326 case GST_MESSAGE_STEP_DONE:
3331 gboolean flush, intermediate, eos;
3334 GST_INFO_OBJECT (playsink, "Handling step-done message");
3335 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3336 &intermediate, &duration, &eos);
3338 if (format == GST_FORMAT_BUFFERS) {
3339 /* for the buffer format, we align the other streams */
3340 if (playsink->audiochain) {
3344 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3347 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3348 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3352 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3356 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3361 /* Send an event to our sinks until one of them works; don't then send to the
3362 * remaining sinks (unlike GstBin)
3363 * Special case: If a text sink is set we need to send the event
3364 * to them in case it's source is different from the a/v stream's source.
3367 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3369 gboolean res = TRUE;
3371 if (playsink->textchain && playsink->textchain->sink) {
3372 gst_event_ref (event);
3373 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3374 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3376 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3380 if (playsink->videochain) {
3381 gst_event_ref (event);
3382 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3383 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3386 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3388 if (playsink->audiochain) {
3389 gst_event_ref (event);
3390 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3391 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3394 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3398 gst_event_unref (event);
3402 /* We only want to send the event to a single sink (overriding GstBin's
3403 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3404 * events appropriately. So, this is a messy duplication of code. */
3406 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3408 gboolean res = FALSE;
3409 GstEventType event_type = GST_EVENT_TYPE (event);
3410 GstPlaySink *playsink;
3412 playsink = GST_PLAY_SINK_CAST (element);
3414 switch (event_type) {
3415 case GST_EVENT_SEEK:
3416 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3417 res = gst_play_sink_send_event_to_sink (playsink, event);
3419 case GST_EVENT_STEP:
3424 gboolean flush, intermediate;
3426 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3429 if (format == GST_FORMAT_BUFFERS) {
3430 /* for buffers, we will try to step video frames, for other formats we
3431 * send the step to all sinks */
3432 res = gst_play_sink_send_event_to_sink (playsink, event);
3435 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3442 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3449 static GstStateChangeReturn
3450 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3452 GstStateChangeReturn ret;
3453 GstStateChangeReturn bret;
3455 GstPlaySink *playsink;
3457 playsink = GST_PLAY_SINK (element);
3459 switch (transition) {
3460 case GST_STATE_CHANGE_READY_TO_PAUSED:
3461 playsink->need_async_start = TRUE;
3462 /* we want to go async to PAUSED until we managed to configure and add the
3464 do_async_start (playsink);
3465 ret = GST_STATE_CHANGE_ASYNC;
3467 case GST_STATE_CHANGE_PAUSED_TO_READY:
3468 /* unblock all pads here */
3469 GST_PLAY_SINK_LOCK (playsink);
3470 video_set_blocked (playsink, FALSE);
3471 audio_set_blocked (playsink, FALSE);
3472 text_set_blocked (playsink, FALSE);
3473 GST_PLAY_SINK_UNLOCK (playsink);
3475 case GST_STATE_CHANGE_READY_TO_NULL:
3476 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3477 /* remove our links to the mute and volume elements when they were
3478 * provided by a sink */
3479 disconnect_chain (playsink->audiochain, playsink);
3480 playsink->audiochain->volume = NULL;
3481 playsink->audiochain->mute = NULL;
3484 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3485 gst_object_unref (playsink->audiochain->ts_offset);
3486 playsink->audiochain->ts_offset = NULL;
3489 if (playsink->videochain && playsink->videochain->ts_offset) {
3490 gst_object_unref (playsink->videochain->ts_offset);
3491 playsink->videochain->ts_offset = NULL;
3493 ret = GST_STATE_CHANGE_SUCCESS;
3496 /* all other state changes return SUCCESS by default, this value can be
3497 * overridden by the result of the children */
3498 ret = GST_STATE_CHANGE_SUCCESS;
3502 /* do the state change of the children */
3504 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3506 /* now look at the result of our children and adjust the return value */
3508 case GST_STATE_CHANGE_FAILURE:
3509 /* failure, we stop */
3510 goto activate_failed;
3511 case GST_STATE_CHANGE_NO_PREROLL:
3512 /* some child returned NO_PREROLL. This is strange but we never know. We
3513 * commit our async state change (if any) and return the NO_PREROLL */
3514 do_async_done (playsink);
3517 case GST_STATE_CHANGE_ASYNC:
3518 /* some child was async, return this */
3522 /* return our previously configured return value */
3526 switch (transition) {
3527 case GST_STATE_CHANGE_READY_TO_PAUSED:
3529 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3530 /* FIXME Release audio device when we implement that */
3531 playsink->need_async_start = TRUE;
3533 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3534 if (playsink->video_sinkpad_stream_synchronizer) {
3535 gst_element_release_request_pad (GST_ELEMENT_CAST
3536 (playsink->stream_synchronizer),
3537 playsink->video_sinkpad_stream_synchronizer);
3538 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3539 playsink->video_sinkpad_stream_synchronizer = NULL;
3540 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3541 playsink->video_srcpad_stream_synchronizer = NULL;
3543 if (playsink->audio_sinkpad_stream_synchronizer) {
3544 gst_element_release_request_pad (GST_ELEMENT_CAST
3545 (playsink->stream_synchronizer),
3546 playsink->audio_sinkpad_stream_synchronizer);
3547 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3548 playsink->audio_sinkpad_stream_synchronizer = NULL;
3549 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3550 playsink->audio_srcpad_stream_synchronizer = NULL;
3552 if (playsink->text_sinkpad_stream_synchronizer) {
3553 gst_element_release_request_pad (GST_ELEMENT_CAST
3554 (playsink->stream_synchronizer),
3555 playsink->text_sinkpad_stream_synchronizer);
3556 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3557 playsink->text_sinkpad_stream_synchronizer = NULL;
3558 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3559 playsink->text_srcpad_stream_synchronizer = NULL;
3563 case GST_STATE_CHANGE_READY_TO_NULL:
3564 /* remove sinks we added */
3565 if (playsink->videodeinterlacechain) {
3566 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3568 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3570 if (playsink->videochain) {
3571 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3572 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3574 if (playsink->audiochain) {
3575 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3576 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3578 if (playsink->vischain) {
3579 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3580 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3582 if (playsink->textchain) {
3583 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3584 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3586 do_async_done (playsink);
3587 /* when going to READY, keep elements around as long as possible,
3588 * so they may be re-used faster next time/url around.
3589 * when really going to NULL, clean up everything completely. */
3590 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3592 /* Unparent the sinks to allow reuse */
3593 if (playsink->videochain && playsink->videochain->sink)
3594 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3595 playsink->videochain->sink);
3596 if (playsink->audiochain && playsink->audiochain->sink)
3597 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3598 playsink->audiochain->sink);
3599 if (playsink->textchain && playsink->textchain->sink)
3600 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3601 playsink->textchain->sink);
3603 if (playsink->audio_sink != NULL)
3604 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3605 if (playsink->video_sink != NULL)
3606 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3607 if (playsink->visualisation != NULL)
3608 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3609 if (playsink->text_sink != NULL)
3610 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3612 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3613 playsink->videodeinterlacechain = NULL;
3614 free_chain ((GstPlayChain *) playsink->videochain);
3615 playsink->videochain = NULL;
3616 free_chain ((GstPlayChain *) playsink->audiochain);
3617 playsink->audiochain = NULL;
3618 free_chain ((GstPlayChain *) playsink->vischain);
3619 playsink->vischain = NULL;
3620 free_chain ((GstPlayChain *) playsink->textchain);
3621 playsink->textchain = NULL;
3632 GST_DEBUG_OBJECT (element,
3633 "element failed to change states -- activation problem?");
3634 return GST_STATE_CHANGE_FAILURE;
3639 gst_play_sink_set_property (GObject * object, guint prop_id,
3640 const GValue * value, GParamSpec * spec)
3642 GstPlaySink *playsink = GST_PLAY_SINK (object);
3646 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3649 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3652 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3654 case PROP_FONT_DESC:
3655 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3657 case PROP_SUBTITLE_ENCODING:
3658 gst_play_sink_set_subtitle_encoding (playsink,
3659 g_value_get_string (value));
3661 case PROP_VIS_PLUGIN:
3662 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3664 case PROP_AV_OFFSET:
3665 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3667 case PROP_VIDEO_SINK:
3668 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3669 g_value_get_object (value));
3671 case PROP_AUDIO_SINK:
3672 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3673 g_value_get_object (value));
3675 case PROP_TEXT_SINK:
3676 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3677 g_value_get_object (value));
3680 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3686 gst_play_sink_get_property (GObject * object, guint prop_id,
3687 GValue * value, GParamSpec * spec)
3689 GstPlaySink *playsink = GST_PLAY_SINK (object);
3693 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3696 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3699 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3701 case PROP_FONT_DESC:
3702 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3704 case PROP_SUBTITLE_ENCODING:
3705 g_value_take_string (value,
3706 gst_play_sink_get_subtitle_encoding (playsink));
3708 case PROP_VIS_PLUGIN:
3709 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3712 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
3714 case PROP_AV_OFFSET:
3715 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3717 case PROP_VIDEO_SINK:
3718 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3719 GST_PLAY_SINK_TYPE_VIDEO));
3721 case PROP_AUDIO_SINK:
3722 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3723 GST_PLAY_SINK_TYPE_AUDIO));
3725 case PROP_TEXT_SINK:
3726 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3727 GST_PLAY_SINK_TYPE_TEXT));
3730 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3737 gst_play_sink_plugin_init (GstPlugin * plugin)
3739 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3741 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3742 GST_TYPE_PLAY_SINK);