2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
28 #include <gst/gst-i18n-plugin.h>
29 #include <gst/pbutils/pbutils.h>
30 #include <gst/video/video.h>
32 #include "gstplaysink.h"
33 #include "gststreamsynchronizer.h"
34 #include "gstplaysinkvideoconvert.h"
35 #include "gstplaysinkaudioconvert.h"
37 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
38 #define GST_CAT_DEFAULT gst_play_sink_debug
40 #define VOLUME_MAX_DOUBLE 10.0
42 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
43 GST_PLAY_FLAG_SOFT_VOLUME
45 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
47 /* holds the common data fields for the audio and video pipelines. We keep them
48 * in a structure to more easily have all the info available. */
51 GstPlaySink *playsink;
64 GstElement *volume; /* element with the volume property */
65 gboolean sink_volume; /* if the volume was provided by the sink */
66 GstElement *mute; /* element with the mute property */
68 GstElement *ts_offset;
74 GstPad *sinkpad, *srcpad;
76 GstElement *deinterlace;
77 } GstPlayVideoDeinterlaceChain;
87 GstElement *ts_offset;
97 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
98 GstPad *vissinkpad; /* visualisation sinkpad, */
100 GstPad *vissrcpad; /* visualisation srcpad, */
101 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
110 GstElement *identity;
112 GstPad *videosinkpad;
114 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
116 GstElement *sink; /* custom sink to receive subtitle buffers */
119 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
120 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
121 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
122 g_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
123 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
125 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
126 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
127 g_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
130 #define PENDING_FLAG_SET(playsink, flagtype) \
131 ((playsink->pending_blocked_pads) |= (1 << flagtype))
132 #define PENDING_FLAG_UNSET(playsink, flagtype) \
133 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
134 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
135 ((playsink->pending_blocked_pads) & (1 << flagtype))
136 #define PENDING_VIDEO_BLOCK(playsink) \
137 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
138 #define PENDING_AUDIO_BLOCK(playsink) \
139 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
140 #define PENDING_TEXT_BLOCK(playsink) \
141 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
149 gboolean async_pending;
150 gboolean need_async_start;
154 GstStreamSynchronizer *stream_synchronizer;
157 GstPlayAudioChain *audiochain;
158 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
159 GstPlayVideoChain *videochain;
160 GstPlayVisChain *vischain;
161 GstPlayTextChain *textchain;
165 gboolean audio_pad_raw;
166 gboolean audio_pad_blocked;
167 GstPad *audio_srcpad_stream_synchronizer;
168 GstPad *audio_sinkpad_stream_synchronizer;
169 gulong audio_block_id;
171 GstElement *audio_tee;
172 GstPad *audio_tee_sink;
173 GstPad *audio_tee_asrc;
174 GstPad *audio_tee_vissrc;
177 gboolean video_pad_raw;
178 gboolean video_pad_blocked;
179 GstPad *video_srcpad_stream_synchronizer;
180 GstPad *video_sinkpad_stream_synchronizer;
181 gulong video_block_id;
184 gboolean text_pad_blocked;
185 GstPad *text_srcpad_stream_synchronizer;
186 GstPad *text_sinkpad_stream_synchronizer;
187 gulong text_block_id;
189 guint32 pending_blocked_pads;
192 GstElement *audio_sink;
193 GstElement *video_sink;
194 GstElement *visualisation;
195 GstElement *text_sink;
198 gchar *font_desc; /* font description */
199 gchar *subtitle_encoding; /* subtitle encoding */
200 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
202 gboolean volume_changed; /* volume/mute changed while no audiochain */
203 gboolean mute_changed; /* ... has been created yet */
207 struct _GstPlaySinkClass
209 GstBinClass parent_class;
211 gboolean (*reconfigure) (GstPlaySink * playsink);
213 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
217 static GstStaticPadTemplate audiotemplate =
218 GST_STATIC_PAD_TEMPLATE ("audio_sink",
221 GST_STATIC_CAPS_ANY);
222 static GstStaticPadTemplate videotemplate =
223 GST_STATIC_PAD_TEMPLATE ("video_sink",
226 GST_STATIC_CAPS_ANY);
227 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
230 GST_STATIC_CAPS_ANY);
232 /* FIXME 0.11: Remove */
233 static GstStaticPadTemplate audiorawtemplate =
234 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
237 GST_STATIC_CAPS_ANY);
238 static GstStaticPadTemplate videorawtemplate =
239 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
242 GST_STATIC_CAPS_ANY);
253 PROP_SUBTITLE_ENCODING,
269 static void gst_play_sink_dispose (GObject * object);
270 static void gst_play_sink_finalize (GObject * object);
271 static void gst_play_sink_set_property (GObject * object, guint prop_id,
272 const GValue * value, GParamSpec * spec);
273 static void gst_play_sink_get_property (GObject * object, guint prop_id,
274 GValue * value, GParamSpec * spec);
276 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
277 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
278 static void gst_play_sink_release_request_pad (GstElement * element,
280 static gboolean gst_play_sink_send_event (GstElement * element,
282 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
283 GstStateChange transition);
285 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
287 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
288 GstPlaySink * playsink);
289 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
290 GstPlaySink * playsink);
292 static void update_av_offset (GstPlaySink * playsink);
295 gst_play_marshal_SAMPLE__BOXED (GClosure * closure,
296 GValue * return_value G_GNUC_UNUSED,
297 guint n_param_values,
298 const GValue * param_values,
299 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
301 typedef GstSample *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
302 gpointer arg_1, gpointer data2);
303 register GMarshalFunc_OBJECT__BOXED callback;
304 register GCClosure *cc = (GCClosure *) closure;
305 register gpointer data1, data2;
307 g_return_if_fail (return_value != NULL);
308 g_return_if_fail (n_param_values == 2);
310 if (G_CCLOSURE_SWAP_DATA (closure)) {
311 data1 = closure->data;
312 data2 = g_value_peek_pointer (param_values + 0);
314 data1 = g_value_peek_pointer (param_values + 0);
315 data2 = closure->data;
318 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
320 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
322 gst_value_take_sample (return_value, v_return);
325 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
327 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
330 gst_play_sink_class_init (GstPlaySinkClass * klass)
332 GObjectClass *gobject_klass;
333 GstElementClass *gstelement_klass;
334 GstBinClass *gstbin_klass;
336 gobject_klass = (GObjectClass *) klass;
337 gstelement_klass = (GstElementClass *) klass;
338 gstbin_klass = (GstBinClass *) klass;
340 gobject_klass->dispose = gst_play_sink_dispose;
341 gobject_klass->finalize = gst_play_sink_finalize;
342 gobject_klass->set_property = gst_play_sink_set_property;
343 gobject_klass->get_property = gst_play_sink_get_property;
349 * Control the behaviour of playsink.
351 g_object_class_install_property (gobject_klass, PROP_FLAGS,
352 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
353 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
354 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
357 * GstPlaySink:volume:
359 * Get or set the current audio stream volume. 1.0 means 100%,
360 * 0.0 means mute. This uses a linear volume scale.
363 g_object_class_install_property (gobject_klass, PROP_VOLUME,
364 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
365 0.0, VOLUME_MAX_DOUBLE, 1.0,
366 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
367 g_object_class_install_property (gobject_klass, PROP_MUTE,
368 g_param_spec_boolean ("mute", "Mute",
369 "Mute the audio channel without changing the volume", FALSE,
370 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
371 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
372 g_param_spec_string ("subtitle-font-desc",
373 "Subtitle font description",
374 "Pango font description of font "
375 "to be used for subtitle rendering", NULL,
376 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
377 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
378 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
379 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
380 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
381 "be checked for an encoding to use. If that is not set either, "
382 "ISO-8859-15 will be assumed.", NULL,
383 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
384 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
385 g_param_spec_object ("vis-plugin", "Vis plugin",
386 "the visualization element to use (NULL = default)",
387 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
389 * GstPlaySink:sample:
391 * Get the currently rendered or prerolled sample in the video sink.
392 * The #GstCaps in the sample will describe the format of the buffer.
394 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
395 g_param_spec_boxed ("sample", "Sample",
396 "The last sample (NULL = no video available)",
397 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
399 * GstPlaySink:av-offset:
401 * Control the synchronisation offset between the audio and video streams.
402 * Positive values make the audio ahead of the video and negative values make
403 * the audio go behind the video.
407 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
408 g_param_spec_int64 ("av-offset", "AV Offset",
409 "The synchronisation offset between audio and video in nanoseconds",
410 G_MININT64, G_MAXINT64, 0,
411 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
414 * GstPlaySink:video-sink:
416 * Set the used video sink element. NULL will use the default sink. playsink
417 * must be in %GST_STATE_NULL
421 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
422 g_param_spec_object ("video-sink", "Video Sink",
423 "the video output element to use (NULL = default sink)",
424 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
426 * GstPlaySink:audio-sink:
428 * Set the used audio sink element. NULL will use the default sink. playsink
429 * must be in %GST_STATE_NULL
433 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
434 g_param_spec_object ("audio-sink", "Audio Sink",
435 "the audio output element to use (NULL = default sink)",
436 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
438 * GstPlaySink:text-sink:
440 * Set the used text sink element. NULL will use the default sink. playsink
441 * must be in %GST_STATE_NULL
445 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
446 g_param_spec_object ("text-sink", "Text sink",
447 "the text output element to use (NULL = default textoverlay)",
448 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
451 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
452 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
453 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
456 * GstPlaySink::convert-sample
457 * @playsink: a #GstPlaySink
458 * @caps: the target format of the sample
460 * Action signal to retrieve the currently playing video sample in the format
461 * specified by @caps.
462 * If @caps is %NULL, no conversion will be performed and this function is
463 * equivalent to the #GstPlaySink::sample property.
465 * Returns: a #GstSample of the current video sample converted to #caps.
466 * The caps in the sample will describe the final layout of the buffer data.
467 * %NULL is returned when no current sample can be retrieved or when the
470 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
471 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
472 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
473 gst_play_marshal_SAMPLE__BOXED, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
475 gst_element_class_add_pad_template (gstelement_klass,
476 gst_static_pad_template_get (&audiorawtemplate));
477 gst_element_class_add_pad_template (gstelement_klass,
478 gst_static_pad_template_get (&audiotemplate));
479 gst_element_class_add_pad_template (gstelement_klass,
480 gst_static_pad_template_get (&videorawtemplate));
481 gst_element_class_add_pad_template (gstelement_klass,
482 gst_static_pad_template_get (&videotemplate));
483 gst_element_class_add_pad_template (gstelement_klass,
484 gst_static_pad_template_get (&texttemplate));
485 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
487 "Convenience sink for multiple streams",
488 "Wim Taymans <wim.taymans@gmail.com>");
490 gstelement_klass->change_state =
491 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
492 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
493 gstelement_klass->request_new_pad =
494 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
495 gstelement_klass->release_pad =
496 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
498 gstbin_klass->handle_message =
499 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
501 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
502 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
506 gst_play_sink_init (GstPlaySink * playsink)
509 playsink->video_sink = NULL;
510 playsink->audio_sink = NULL;
511 playsink->visualisation = NULL;
512 playsink->text_sink = NULL;
513 playsink->volume = 1.0;
514 playsink->font_desc = NULL;
515 playsink->subtitle_encoding = NULL;
516 playsink->flags = DEFAULT_FLAGS;
518 playsink->stream_synchronizer =
519 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
520 gst_bin_add (GST_BIN_CAST (playsink),
521 GST_ELEMENT_CAST (playsink->stream_synchronizer));
523 g_rec_mutex_init (&playsink->lock);
524 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
528 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
532 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
535 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
541 free_chain (GstPlayChain * chain)
545 gst_object_unref (chain->bin);
551 gst_play_sink_dispose (GObject * object)
553 GstPlaySink *playsink;
555 playsink = GST_PLAY_SINK (object);
557 if (playsink->audio_sink != NULL) {
558 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
559 gst_object_unref (playsink->audio_sink);
560 playsink->audio_sink = NULL;
562 if (playsink->video_sink != NULL) {
563 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
564 gst_object_unref (playsink->video_sink);
565 playsink->video_sink = NULL;
567 if (playsink->visualisation != NULL) {
568 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
569 gst_object_unref (playsink->visualisation);
570 playsink->visualisation = NULL;
572 if (playsink->text_sink != NULL) {
573 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
574 gst_object_unref (playsink->text_sink);
575 playsink->text_sink = NULL;
578 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
579 playsink->videodeinterlacechain = NULL;
580 free_chain ((GstPlayChain *) playsink->videochain);
581 playsink->videochain = NULL;
582 free_chain ((GstPlayChain *) playsink->audiochain);
583 playsink->audiochain = NULL;
584 free_chain ((GstPlayChain *) playsink->vischain);
585 playsink->vischain = NULL;
586 free_chain ((GstPlayChain *) playsink->textchain);
587 playsink->textchain = NULL;
589 if (playsink->audio_tee_sink) {
590 gst_object_unref (playsink->audio_tee_sink);
591 playsink->audio_tee_sink = NULL;
594 if (playsink->audio_tee_vissrc) {
595 gst_element_release_request_pad (playsink->audio_tee,
596 playsink->audio_tee_vissrc);
597 gst_object_unref (playsink->audio_tee_vissrc);
598 playsink->audio_tee_vissrc = NULL;
601 if (playsink->audio_tee_asrc) {
602 gst_element_release_request_pad (playsink->audio_tee,
603 playsink->audio_tee_asrc);
604 gst_object_unref (playsink->audio_tee_asrc);
605 playsink->audio_tee_asrc = NULL;
608 g_free (playsink->font_desc);
609 playsink->font_desc = NULL;
611 g_free (playsink->subtitle_encoding);
612 playsink->subtitle_encoding = NULL;
614 playsink->stream_synchronizer = NULL;
616 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
620 gst_play_sink_finalize (GObject * object)
622 GstPlaySink *playsink;
624 playsink = GST_PLAY_SINK (object);
626 g_rec_mutex_clear (&playsink->lock);
628 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
632 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
635 GstElement **elem = NULL, *old = NULL;
637 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
639 GST_PLAY_SINK_LOCK (playsink);
641 case GST_PLAY_SINK_TYPE_AUDIO:
642 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
643 elem = &playsink->audio_sink;
645 case GST_PLAY_SINK_TYPE_VIDEO:
646 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
647 elem = &playsink->video_sink;
649 case GST_PLAY_SINK_TYPE_TEXT:
650 elem = &playsink->text_sink;
658 gst_object_ref (sink);
661 GST_PLAY_SINK_UNLOCK (playsink);
665 gst_element_set_state (old, GST_STATE_NULL);
666 gst_object_unref (old);
671 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
673 GstElement *result = NULL;
674 GstElement *elem = NULL, *chainp = NULL;
676 GST_PLAY_SINK_LOCK (playsink);
678 case GST_PLAY_SINK_TYPE_AUDIO:
679 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
681 GstPlayAudioChain *chain;
682 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
683 chainp = chain->sink;
684 elem = playsink->audio_sink;
687 case GST_PLAY_SINK_TYPE_VIDEO:
688 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
690 GstPlayVideoChain *chain;
691 if ((chain = (GstPlayVideoChain *) playsink->videochain))
692 chainp = chain->sink;
693 elem = playsink->video_sink;
696 case GST_PLAY_SINK_TYPE_TEXT:
698 GstPlayTextChain *chain;
699 if ((chain = (GstPlayTextChain *) playsink->textchain))
700 chainp = chain->sink;
701 elem = playsink->text_sink;
708 /* we have an active chain with a sink, get the sink */
709 result = gst_object_ref (chainp);
711 /* nothing found, return last configured sink */
712 if (result == NULL && elem)
713 result = gst_object_ref (elem);
714 GST_PLAY_SINK_UNLOCK (playsink);
719 static GstPadProbeReturn
720 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
723 GstPlaySink *playsink;
724 GstPlayVisChain *chain;
726 playsink = GST_PLAY_SINK (user_data);
728 GST_PLAY_SINK_LOCK (playsink);
729 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
730 /* now try to change the plugin in the running vis chain */
731 if (!(chain = (GstPlayVisChain *) playsink->vischain))
734 /* unlink the old plugin and unghost the pad */
735 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
736 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
738 /* set the old plugin to NULL and remove */
739 gst_element_set_state (chain->vis, GST_STATE_NULL);
740 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
742 /* add new plugin and set state to playing */
743 chain->vis = playsink->visualisation;
744 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
745 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
748 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
749 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
752 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
753 GST_PAD_LINK_CHECK_NOTHING);
754 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
758 GST_PLAY_SINK_UNLOCK (playsink);
760 /* remove the probe and unblock the pad */
761 return GST_PAD_PROBE_REMOVE;
765 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
767 GstPlayVisChain *chain;
769 /* setting NULL means creating the default vis plugin */
771 vis = gst_element_factory_make ("goom", "vis");
773 /* simply return if we don't have a vis plugin here */
777 GST_PLAY_SINK_LOCK (playsink);
778 /* first store the new vis */
779 if (playsink->visualisation)
780 gst_object_unref (playsink->visualisation);
782 gst_object_ref_sink (vis);
783 playsink->visualisation = vis;
785 /* now try to change the plugin in the running vis chain, if we have no chain,
786 * we don't bother, any future vis chain will be created with the new vis
788 if (!(chain = (GstPlayVisChain *) playsink->vischain))
791 /* block the pad, the next time the callback is called we can change the
792 * visualisation. It's possible that this never happens or that the pad was
793 * already blocked. If the callback never happens, we don't have new data so
794 * we don't need the new vis plugin. If the pad was already blocked, the
795 * function returns FALSE but the previous pad block will do the right thing
797 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
798 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
799 gst_play_sink_vis_blocked, playsink, NULL);
801 GST_PLAY_SINK_UNLOCK (playsink);
807 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
809 GstElement *result = NULL;
810 GstPlayVisChain *chain;
812 GST_PLAY_SINK_LOCK (playsink);
813 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
814 /* we have an active chain, get the sink */
816 result = gst_object_ref (chain->vis);
818 /* nothing found, return last configured sink */
819 if (result == NULL && playsink->visualisation)
820 result = gst_object_ref (playsink->visualisation);
821 GST_PLAY_SINK_UNLOCK (playsink);
827 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
829 GstPlayAudioChain *chain;
831 GST_PLAY_SINK_LOCK (playsink);
832 playsink->volume = volume;
833 chain = (GstPlayAudioChain *) playsink->audiochain;
834 if (chain && chain->volume) {
835 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
836 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
837 chain->mute, volume, playsink->mute);
838 /* if there is a mute element or we are not muted, set the volume */
839 if (chain->mute || !playsink->mute)
840 g_object_set (chain->volume, "volume", volume, NULL);
842 GST_LOG_OBJECT (playsink, "no volume element");
843 playsink->volume_changed = TRUE;
845 GST_PLAY_SINK_UNLOCK (playsink);
849 gst_play_sink_get_volume (GstPlaySink * playsink)
852 GstPlayAudioChain *chain;
854 GST_PLAY_SINK_LOCK (playsink);
855 chain = (GstPlayAudioChain *) playsink->audiochain;
856 result = playsink->volume;
857 if (chain && chain->volume) {
858 if (chain->mute || !playsink->mute) {
859 g_object_get (chain->volume, "volume", &result, NULL);
860 playsink->volume = result;
863 GST_PLAY_SINK_UNLOCK (playsink);
869 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
871 GstPlayAudioChain *chain;
873 GST_PLAY_SINK_LOCK (playsink);
874 playsink->mute = mute;
875 chain = (GstPlayAudioChain *) playsink->audiochain;
878 g_object_set (chain->mute, "mute", mute, NULL);
879 } else if (chain->volume) {
881 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
883 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
887 playsink->mute_changed = TRUE;
889 GST_PLAY_SINK_UNLOCK (playsink);
893 gst_play_sink_get_mute (GstPlaySink * playsink)
896 GstPlayAudioChain *chain;
898 GST_PLAY_SINK_LOCK (playsink);
899 chain = (GstPlayAudioChain *) playsink->audiochain;
900 if (chain && chain->mute) {
901 g_object_get (chain->mute, "mute", &result, NULL);
902 playsink->mute = result;
904 result = playsink->mute;
906 GST_PLAY_SINK_UNLOCK (playsink);
912 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
916 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
917 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
921 add_chain (GstPlayChain * chain, gboolean add)
923 if (chain->added == add)
927 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
929 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
930 /* we don't want to lose our sink status */
931 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
940 activate_chain (GstPlayChain * chain, gboolean activate)
944 if (chain->activated == activate)
947 GST_OBJECT_LOCK (chain->playsink);
948 state = GST_STATE_TARGET (chain->playsink);
949 GST_OBJECT_UNLOCK (chain->playsink);
952 gst_element_set_state (chain->bin, state);
954 gst_element_set_state (chain->bin, GST_STATE_NULL);
956 chain->activated = activate;
962 element_is_sink (GstElement * element)
966 GST_OBJECT_LOCK (element);
967 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
968 GST_OBJECT_UNLOCK (element);
970 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
975 element_has_property (GstElement * element, const gchar * pname, GType type)
979 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
982 GST_DEBUG_OBJECT (element, "no %s property", pname);
986 if (type == G_TYPE_INVALID || type == pspec->value_type ||
987 g_type_is_a (pspec->value_type, type)) {
988 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
989 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
993 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
994 "and we expected it to be of type %s", pname,
995 g_type_name (pspec->value_type), g_type_name (type));
1002 const gchar *prop_name;
1005 } FindPropertyHelper;
1008 find_property (const GValue * item, FindPropertyHelper * helper)
1010 GstElement *element = g_value_get_object (item);
1011 if (helper->need_sink && !element_is_sink (element)) {
1015 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1019 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1020 (helper->need_sink) ? "sink" : "element");
1021 return 0; /* keep it */
1024 /* FIXME: why not move these functions into core? */
1025 /* find a sink in the hierarchy with a property named @name. This function does
1026 * not increase the refcount of the returned object and thus remains valid as
1027 * long as the bin is valid. */
1029 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1030 const gchar * name, GType expected_type)
1032 GstElement *result = NULL;
1035 if (element_has_property (obj, name, expected_type)) {
1037 } else if (GST_IS_BIN (obj)) {
1039 GValue item = { 0, };
1040 FindPropertyHelper helper = { name, expected_type, TRUE };
1042 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1043 found = gst_iterator_find_custom (it,
1044 (GCompareFunc) find_property, &item, &helper);
1045 gst_iterator_free (it);
1047 result = g_value_get_object (&item);
1048 /* we don't need the extra ref */
1049 g_value_unset (&item);
1055 /* find an object in the hierarchy with a property named @name */
1057 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1058 const gchar * name, GType expected_type)
1060 GstElement *result = NULL;
1063 if (GST_IS_BIN (obj)) {
1065 GValue item = { 0, };
1066 FindPropertyHelper helper = { name, expected_type, FALSE };
1068 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1069 found = gst_iterator_find_custom (it,
1070 (GCompareFunc) find_property, &item, &helper);
1071 gst_iterator_free (it);
1073 result = g_value_dup_object (&item);
1074 g_value_unset (&item);
1076 if (element_has_property (obj, name, expected_type)) {
1078 gst_object_ref (obj);
1085 do_async_start (GstPlaySink * playsink)
1087 GstMessage *message;
1089 if (!playsink->need_async_start) {
1090 GST_INFO_OBJECT (playsink, "no async_start needed");
1094 playsink->async_pending = TRUE;
1096 GST_INFO_OBJECT (playsink, "Sending async_start message");
1097 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1098 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1099 (playsink), message);
1103 do_async_done (GstPlaySink * playsink)
1105 GstMessage *message;
1107 if (playsink->async_pending) {
1108 GST_INFO_OBJECT (playsink, "Sending async_done message");
1109 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1110 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1111 (playsink), message);
1113 playsink->async_pending = FALSE;
1116 playsink->need_async_start = FALSE;
1119 /* try to change the state of an element. This function returns the element when
1120 * the state change could be performed. When this function returns NULL an error
1121 * occured and the element is unreffed if @unref is TRUE. */
1123 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1125 GstStateChangeReturn ret;
1128 ret = gst_element_set_state (element, GST_STATE_READY);
1129 if (ret == GST_STATE_CHANGE_FAILURE) {
1130 GST_DEBUG_OBJECT (playsink, "failed state change..");
1131 gst_element_set_state (element, GST_STATE_NULL);
1133 gst_object_unref (element);
1140 /* make the element (bin) that contains the elements needed to perform
1141 * video display. Only used for *raw* video streams.
1143 * +------------------------------------------------------------+
1145 * | +-------+ +----------+ +----------+ +---------+ |
1146 * | | queue | |colorspace| |videoscale| |videosink| |
1147 * | +-sink src-sink src-sink src-sink | |
1148 * | | +-------+ +----------+ +----------+ +---------+ |
1150 * +------------------------------------------------------------+
1153 static GstPlayVideoDeinterlaceChain *
1154 gen_video_deinterlace_chain (GstPlaySink * playsink)
1156 GstPlayVideoDeinterlaceChain *chain;
1159 GstElement *head = NULL, *prev = NULL;
1161 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1162 chain->chain.playsink = playsink;
1164 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1166 /* create a bin to hold objects, as we create them we add them to this bin so
1167 * that when something goes wrong we only need to unref the bin */
1168 chain->chain.bin = gst_bin_new ("vdbin");
1169 bin = GST_BIN_CAST (chain->chain.bin);
1170 gst_object_ref_sink (bin);
1172 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1173 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1174 if (chain->conv == NULL) {
1175 post_missing_element_message (playsink, COLORSPACE);
1176 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1177 (_("Missing element '%s' - check your GStreamer installation."),
1178 COLORSPACE), ("video rendering might fail"));
1180 gst_bin_add (bin, chain->conv);
1185 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1186 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1187 if (chain->deinterlace == NULL) {
1188 post_missing_element_message (playsink, "deinterlace");
1189 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1190 (_("Missing element '%s' - check your GStreamer installation."),
1191 "deinterlace"), ("deinterlacing won't work"));
1193 gst_bin_add (bin, chain->deinterlace);
1195 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1196 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1199 head = chain->deinterlace;
1201 prev = chain->deinterlace;
1205 pad = gst_element_get_static_pad (head, "sink");
1206 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1207 gst_object_unref (pad);
1209 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1213 pad = gst_element_get_static_pad (prev, "src");
1214 chain->srcpad = gst_ghost_pad_new ("src", pad);
1215 gst_object_unref (pad);
1217 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1220 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1221 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1227 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1228 (NULL), ("Failed to configure the video deinterlace chain."));
1229 free_chain ((GstPlayChain *) chain);
1234 /* make the element (bin) that contains the elements needed to perform
1237 * +------------------------------------------------------------+
1239 * | +-------+ +----------+ +----------+ +---------+ |
1240 * | | queue | |colorspace| |videoscale| |videosink| |
1241 * | +-sink src-sink src-sink src-sink | |
1242 * | | +-------+ +----------+ +----------+ +---------+ |
1244 * +------------------------------------------------------------+
1247 static GstPlayVideoChain *
1248 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1250 GstPlayVideoChain *chain;
1253 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1255 chain = g_new0 (GstPlayVideoChain, 1);
1256 chain->chain.playsink = playsink;
1257 chain->chain.raw = raw;
1259 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1261 if (playsink->video_sink) {
1262 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1263 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1265 /* only try fallback if no specific sink was chosen */
1266 if (chain->sink == NULL) {
1267 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1268 elem = gst_element_factory_make ("autovideosink", "videosink");
1269 chain->sink = try_element (playsink, elem, TRUE);
1271 if (chain->sink == NULL) {
1272 /* if default sink from config.h is different then try it too */
1273 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1274 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1275 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1276 chain->sink = try_element (playsink, elem, TRUE);
1280 playsink->video_sink = gst_object_ref (chain->sink);
1282 if (chain->sink == NULL)
1286 /* if we can disable async behaviour of the sink, we can avoid adding a
1287 * queue for the audio chain. */
1289 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1292 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1293 async, GST_ELEMENT_NAME (elem));
1294 g_object_set (elem, "async", async, NULL);
1295 chain->async = async;
1297 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1298 chain->async = TRUE;
1301 /* find ts-offset element */
1302 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1303 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1306 /* create a bin to hold objects, as we create them we add them to this bin so
1307 * that when something goes wrong we only need to unref the bin */
1308 chain->chain.bin = gst_bin_new ("vbin");
1309 bin = GST_BIN_CAST (chain->chain.bin);
1310 gst_object_ref_sink (bin);
1311 gst_bin_add (bin, chain->sink);
1313 /* decouple decoder from sink, this improves playback quite a lot since the
1314 * decoder can continue while the sink blocks for synchronisation. We don't
1315 * need a lot of buffers as this consumes a lot of memory and we don't want
1316 * too little because else we would be context switching too quickly. */
1317 chain->queue = gst_element_factory_make ("queue", "vqueue");
1318 if (chain->queue == NULL) {
1319 post_missing_element_message (playsink, "queue");
1320 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1321 (_("Missing element '%s' - check your GStreamer installation."),
1322 "queue"), ("video rendering might be suboptimal"));
1326 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1327 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1328 gst_bin_add (bin, chain->queue);
1329 head = prev = chain->queue;
1332 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1333 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1335 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1336 gst_bin_add (bin, chain->conv);
1338 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1339 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1348 GST_DEBUG_OBJECT (playsink, "linking to sink");
1349 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1350 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1354 pad = gst_element_get_static_pad (head, "sink");
1355 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1356 gst_object_unref (pad);
1358 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1365 if (!elem && !playsink->video_sink) {
1366 post_missing_element_message (playsink, "autovideosink");
1367 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1368 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1369 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1370 (_("Both autovideosink and %s elements are missing."),
1371 DEFAULT_VIDEOSINK), (NULL));
1373 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1374 (_("The autovideosink element is missing.")), (NULL));
1377 if (playsink->video_sink) {
1378 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1379 (_("Configured videosink %s is not working."),
1380 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1381 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1382 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1383 (_("Both autovideosink and %s elements are not working."),
1384 DEFAULT_VIDEOSINK), (NULL));
1386 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1387 (_("The autovideosink element is not working.")), (NULL));
1390 free_chain ((GstPlayChain *) chain);
1395 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1396 (NULL), ("Failed to configure the video sink."));
1397 /* checking sink made it READY */
1398 gst_element_set_state (chain->sink, GST_STATE_NULL);
1399 /* Remove chain from the bin to allow reuse later */
1400 gst_bin_remove (bin, chain->sink);
1401 free_chain ((GstPlayChain *) chain);
1407 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1410 GstPlayVideoChain *chain;
1411 GstStateChangeReturn ret;
1413 chain = playsink->videochain;
1415 chain->chain.raw = raw;
1417 /* if the chain was active we don't do anything */
1418 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1421 /* try to set the sink element to READY again */
1422 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1423 if (ret == GST_STATE_CHANGE_FAILURE)
1426 /* find ts-offset element */
1428 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1429 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1432 /* if we can disable async behaviour of the sink, we can avoid adding a
1433 * queue for the audio chain. */
1435 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1438 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1439 async, GST_ELEMENT_NAME (elem));
1440 g_object_set (elem, "async", async, NULL);
1441 chain->async = async;
1443 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1444 chain->async = TRUE;
1449 /* make an element for playback of video with subtitles embedded.
1450 * Only used for *raw* video streams.
1452 * +--------------------------------------------+
1454 * | +--------+ +-----------------+ |
1455 * | | queue | | subtitleoverlay | |
1456 * video--src sink---video_sink | |
1457 * | +--------+ | src--src
1458 * text------------------text_sink | |
1459 * | +-----------------+ |
1460 * +--------------------------------------------+
1463 static GstPlayTextChain *
1464 gen_text_chain (GstPlaySink * playsink)
1466 GstPlayTextChain *chain;
1469 GstPad *videosinkpad, *textsinkpad, *srcpad;
1471 chain = g_new0 (GstPlayTextChain, 1);
1472 chain->chain.playsink = playsink;
1474 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1476 chain->chain.bin = gst_bin_new ("tbin");
1477 bin = GST_BIN_CAST (chain->chain.bin);
1478 gst_object_ref_sink (bin);
1480 videosinkpad = textsinkpad = srcpad = NULL;
1482 /* first try to hook the text pad to the custom sink */
1483 if (playsink->text_sink) {
1484 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1485 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1488 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1491 /* make sure the sparse subtitles don't participate in the preroll */
1492 g_object_set (elem, "async", FALSE, NULL);
1493 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1494 gst_bin_add (bin, chain->sink);
1495 /* NOTE streamsynchronizer needs streams decoupled */
1496 /* make a little queue */
1497 chain->queue = gst_element_factory_make ("queue", "subqueue");
1498 if (chain->queue == NULL) {
1499 post_missing_element_message (playsink, "queue");
1500 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1501 (_("Missing element '%s' - check your GStreamer installation."),
1502 "queue"), ("rendering might be suboptimal"));
1504 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1505 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1506 "silent", TRUE, NULL);
1507 gst_bin_add (bin, chain->queue);
1509 /* we have a custom sink, this will be our textsinkpad */
1510 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1511 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1512 /* we're all fine now and we can add the sink to the chain */
1513 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1514 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1516 GST_WARNING_OBJECT (playsink,
1517 "can't find a sink pad on custom text sink");
1518 gst_bin_remove (bin, chain->sink);
1519 gst_bin_remove (bin, chain->queue);
1521 chain->queue = NULL;
1523 /* try to set sync to true but it's no biggie when we can't */
1524 if (chain->sink && (elem =
1525 gst_play_sink_find_property_sinks (playsink, chain->sink,
1526 "sync", G_TYPE_BOOLEAN)))
1527 g_object_set (elem, "sync", TRUE, NULL);
1530 gst_bin_remove (bin, chain->sink);
1532 GST_WARNING_OBJECT (playsink,
1533 "can't find async property in custom text sink");
1536 if (textsinkpad == NULL) {
1537 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1538 (_("Custom text sink element is not usable.")),
1539 ("fallback to default textoverlay"));
1543 if (textsinkpad == NULL) {
1544 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1545 /* make a little queue */
1546 chain->queue = gst_element_factory_make ("queue", "vqueue");
1547 if (chain->queue == NULL) {
1548 post_missing_element_message (playsink, "queue");
1549 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1550 (_("Missing element '%s' - check your GStreamer installation."),
1551 "queue"), ("video rendering might be suboptimal"));
1553 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1554 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1555 "silent", TRUE, NULL);
1556 gst_bin_add (bin, chain->queue);
1557 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1561 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1562 if (chain->overlay == NULL) {
1563 post_missing_element_message (playsink, "subtitleoverlay");
1564 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1565 (_("Missing element '%s' - check your GStreamer installation."),
1566 "subtitleoverlay"), ("subtitle rendering disabled"));
1568 GstElement *element;
1570 gst_bin_add (bin, chain->overlay);
1572 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1573 if (playsink->font_desc) {
1574 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1575 playsink->font_desc, NULL);
1577 if (playsink->subtitle_encoding) {
1578 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1579 playsink->subtitle_encoding, NULL);
1582 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1583 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1585 /* make another little queue to decouple streams */
1586 element = gst_element_factory_make ("queue", "subqueue");
1587 if (element == NULL) {
1588 post_missing_element_message (playsink, "queue");
1589 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1590 (_("Missing element '%s' - check your GStreamer installation."),
1591 "queue"), ("rendering might be suboptimal"));
1593 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1594 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1595 "silent", TRUE, NULL);
1596 gst_bin_add (bin, element);
1597 if (gst_element_link_pads_full (element, "src", chain->overlay,
1598 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1599 textsinkpad = gst_element_get_static_pad (element, "sink");
1600 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1602 gst_bin_remove (bin, chain->sink);
1603 gst_bin_remove (bin, chain->overlay);
1605 chain->overlay = NULL;
1606 gst_object_unref (videosinkpad);
1607 videosinkpad = NULL;
1614 if (videosinkpad == NULL) {
1615 /* if we still don't have a videosink, we don't have an overlay. the only
1616 * thing we can do is insert an identity and ghost the src
1618 chain->identity = gst_element_factory_make ("identity", "tidentity");
1619 if (chain->identity == NULL) {
1620 post_missing_element_message (playsink, "identity");
1621 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1622 (_("Missing element '%s' - check your GStreamer installation."),
1623 "identity"), (NULL));
1625 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1626 g_object_set (chain->identity, "silent", TRUE, NULL);
1627 gst_bin_add (bin, chain->identity);
1628 srcpad = gst_element_get_static_pad (chain->identity, "src");
1629 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1633 /* expose the ghostpads */
1635 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1636 gst_object_unref (videosinkpad);
1637 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1640 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1641 gst_object_unref (textsinkpad);
1642 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1645 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1646 gst_object_unref (srcpad);
1647 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1654 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1658 g_object_get (object, "volume", &vol, NULL);
1659 playsink->volume = vol;
1661 g_object_notify (G_OBJECT (playsink), "volume");
1665 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1669 g_object_get (object, "mute", &mute, NULL);
1670 playsink->mute = mute;
1672 g_object_notify (G_OBJECT (playsink), "mute");
1675 /* make the chain that contains the elements needed to perform
1678 * We add a tee as the first element so that we can link the visualisation chain
1679 * to it when requested.
1681 * +-------------------------------------------------------------+
1683 * | +---------+ +----------+ +---------+ +---------+ |
1684 * | |audioconv| |audioscale| | volume | |audiosink| |
1685 * | +-srck src-sink src-sink src-sink | |
1686 * | | +---------+ +----------+ +---------+ +---------+ |
1688 * +-------------------------------------------------------------+
1690 static GstPlayAudioChain *
1691 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1693 GstPlayAudioChain *chain;
1695 gboolean have_volume;
1697 GstElement *head, *prev, *elem = NULL;
1699 chain = g_new0 (GstPlayAudioChain, 1);
1700 chain->chain.playsink = playsink;
1701 chain->chain.raw = raw;
1703 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1705 if (playsink->audio_sink) {
1706 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1707 playsink->audio_sink);
1708 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1710 /* only try fallback if no specific sink was chosen */
1711 if (chain->sink == NULL) {
1712 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1713 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1714 chain->sink = try_element (playsink, elem, TRUE);
1716 if (chain->sink == NULL) {
1717 /* if default sink from config.h is different then try it too */
1718 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1719 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1720 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1721 chain->sink = try_element (playsink, elem, TRUE);
1725 playsink->audio_sink = gst_object_ref (chain->sink);
1727 if (chain->sink == NULL)
1730 chain->chain.bin = gst_bin_new ("abin");
1731 bin = GST_BIN_CAST (chain->chain.bin);
1732 gst_object_ref_sink (bin);
1733 gst_bin_add (bin, chain->sink);
1735 /* we have to add a queue when we need to decouple for the video sink in
1736 * visualisations and for streamsynchronizer */
1737 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1738 chain->queue = gst_element_factory_make ("queue", "aqueue");
1739 if (chain->queue == NULL) {
1740 post_missing_element_message (playsink, "queue");
1741 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1742 (_("Missing element '%s' - check your GStreamer installation."),
1743 "queue"), ("audio playback and visualizations might not work"));
1747 g_object_set (chain->queue, "silent", TRUE, NULL);
1748 gst_bin_add (bin, chain->queue);
1749 prev = head = chain->queue;
1752 /* find ts-offset element */
1753 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1754 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1757 /* check if the sink, or something within the sink, has the volume property.
1758 * If it does we don't need to add a volume element. */
1760 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1763 chain->volume = elem;
1765 g_signal_connect (chain->volume, "notify::volume",
1766 G_CALLBACK (notify_volume_cb), playsink);
1768 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1770 chain->sink_volume = TRUE;
1771 /* if the sink also has a mute property we can use this as well. We'll only
1772 * use the mute property if there is a volume property. We can simulate the
1773 * mute with the volume otherwise. */
1775 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1778 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1779 g_signal_connect (chain->mute, "notify::mute",
1780 G_CALLBACK (notify_mute_cb), playsink);
1782 /* use the sink to control the volume and mute */
1783 if (playsink->volume_changed) {
1784 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1785 playsink->volume_changed = FALSE;
1787 if (playsink->mute_changed) {
1789 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1792 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1794 playsink->mute_changed = FALSE;
1797 /* no volume, we need to add a volume element when we can */
1798 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1799 have_volume = FALSE;
1800 chain->sink_volume = FALSE;
1803 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1804 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1805 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1806 gboolean use_volume =
1807 !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME;
1808 GST_DEBUG_OBJECT (playsink,
1809 "creating audioconvert with use-converters %d, use-volume %d",
1810 use_converters, use_volume);
1812 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1813 "use-converters", use_converters, "use-volume", use_volume, NULL);
1814 gst_bin_add (bin, chain->conv);
1816 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1817 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1824 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1825 GstPlaySinkAudioConvert *conv =
1826 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1829 chain->volume = conv->volume;
1832 g_signal_connect (chain->volume, "notify::volume",
1833 G_CALLBACK (notify_volume_cb), playsink);
1835 /* volume also has the mute property */
1836 chain->mute = chain->volume;
1837 g_signal_connect (chain->mute, "notify::mute",
1838 G_CALLBACK (notify_mute_cb), playsink);
1840 /* configure with the latest volume and mute */
1841 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1843 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1849 /* we only have to link to the previous element if we have something in
1850 * front of the sink */
1851 GST_DEBUG_OBJECT (playsink, "linking to sink");
1852 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1853 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1857 /* post a warning if we have no way to configure the volume */
1859 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1860 (_("No volume control found")), ("Volume/mute is not available"));
1863 /* and ghost the sinkpad of the headmost element */
1864 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1865 pad = gst_element_get_static_pad (head, "sink");
1866 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1867 gst_object_unref (pad);
1868 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1875 if (!elem && !playsink->audio_sink) {
1876 post_missing_element_message (playsink, "autoaudiosink");
1877 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1878 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1879 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1880 (_("Both autoaudiosink and %s elements are missing."),
1881 DEFAULT_AUDIOSINK), (NULL));
1883 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1884 (_("The autoaudiosink element is missing.")), (NULL));
1887 if (playsink->audio_sink) {
1888 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1889 (_("Configured audiosink %s is not working."),
1890 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1891 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1892 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1893 (_("Both autoaudiosink and %s elements are not working."),
1894 DEFAULT_AUDIOSINK), (NULL));
1896 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1897 (_("The autoaudiosink element is not working.")), (NULL));
1900 free_chain ((GstPlayChain *) chain);
1905 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1906 (NULL), ("Failed to configure the audio sink."));
1907 /* checking sink made it READY */
1908 gst_element_set_state (chain->sink, GST_STATE_NULL);
1909 /* Remove chain from the bin to allow reuse later */
1910 gst_bin_remove (bin, chain->sink);
1911 free_chain ((GstPlayChain *) chain);
1917 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1920 GstPlayAudioChain *chain;
1921 GstStateChangeReturn ret;
1923 chain = playsink->audiochain;
1925 chain->chain.raw = raw;
1927 /* if the chain was active we don't do anything */
1928 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1931 /* try to set the sink element to READY again */
1932 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1933 if (ret == GST_STATE_CHANGE_FAILURE)
1936 /* find ts-offset element */
1937 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1938 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1941 /* check if the sink, or something within the sink, has the volume property.
1942 * If it does we don't need to add a volume element. */
1944 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1947 chain->volume = elem;
1949 if (playsink->volume_changed) {
1950 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1952 /* use the sink to control the volume */
1953 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1954 playsink->volume_changed = FALSE;
1957 g_signal_connect (chain->volume, "notify::volume",
1958 G_CALLBACK (notify_volume_cb), playsink);
1959 /* if the sink also has a mute property we can use this as well. We'll only
1960 * use the mute property if there is a volume property. We can simulate the
1961 * mute with the volume otherwise. */
1963 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1966 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1967 g_signal_connect (chain->mute, "notify::mute",
1968 G_CALLBACK (notify_mute_cb), playsink);
1971 g_object_set (chain->conv, "use-volume", FALSE, NULL);
1973 GstPlaySinkAudioConvert *conv =
1974 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1976 /* no volume, we need to add a volume element when we can */
1977 g_object_set (chain->conv, "use-volume", TRUE, NULL);
1978 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1980 /* Disconnect signals */
1981 disconnect_chain (chain, playsink);
1984 chain->volume = conv->volume;
1985 chain->mute = chain->volume;
1987 g_signal_connect (chain->volume, "notify::volume",
1988 G_CALLBACK (notify_volume_cb), playsink);
1990 g_signal_connect (chain->mute, "notify::mute",
1991 G_CALLBACK (notify_mute_cb), playsink);
1993 /* configure with the latest volume and mute */
1994 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1995 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1998 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2004 * +-------------------------------------------------------------------+
2006 * | +----------+ +------------+ +----------+ +-------+ |
2007 * | | visqueue | | audioconv | | audiores | | vis | |
2008 * | +-sink src-sink + samp src-sink src-sink src-+ |
2009 * | | +----------+ +------------+ +----------+ +-------+ | |
2011 * +-------------------------------------------------------------------+
2014 static GstPlayVisChain *
2015 gen_vis_chain (GstPlaySink * playsink)
2017 GstPlayVisChain *chain;
2023 chain = g_new0 (GstPlayVisChain, 1);
2024 chain->chain.playsink = playsink;
2026 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2028 chain->chain.bin = gst_bin_new ("visbin");
2029 bin = GST_BIN_CAST (chain->chain.bin);
2030 gst_object_ref_sink (bin);
2032 /* we're queuing raw audio here, we can remove this queue when we can disable
2033 * async behaviour in the video sink. */
2034 chain->queue = gst_element_factory_make ("queue", "visqueue");
2035 if (chain->queue == NULL)
2037 g_object_set (chain->queue, "silent", TRUE, NULL);
2038 gst_bin_add (bin, chain->queue);
2040 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2041 if (chain->conv == NULL)
2042 goto no_audioconvert;
2043 gst_bin_add (bin, chain->conv);
2045 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2046 if (chain->resample == NULL)
2047 goto no_audioresample;
2048 gst_bin_add (bin, chain->resample);
2050 /* this pad will be used for blocking the dataflow and switching the vis
2052 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2054 if (playsink->visualisation) {
2055 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2056 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2058 if (chain->vis == NULL) {
2059 GST_DEBUG_OBJECT (playsink, "trying goom");
2060 elem = gst_element_factory_make ("goom", "vis");
2061 chain->vis = try_element (playsink, elem, TRUE);
2063 if (chain->vis == NULL)
2066 gst_bin_add (bin, chain->vis);
2068 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2069 GST_PAD_LINK_CHECK_NOTHING);
2071 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2072 GST_PAD_LINK_CHECK_NOTHING);
2074 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2075 GST_PAD_LINK_CHECK_NOTHING);
2079 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2080 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2082 pad = gst_element_get_static_pad (chain->queue, "sink");
2083 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2084 gst_object_unref (pad);
2085 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2087 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2088 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2095 post_missing_element_message (playsink, "queue");
2096 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2097 (_("Missing element '%s' - check your GStreamer installation."),
2099 free_chain ((GstPlayChain *) chain);
2104 post_missing_element_message (playsink, "audioconvert");
2105 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2106 (_("Missing element '%s' - check your GStreamer installation."),
2107 "audioconvert"), ("possibly a liboil version mismatch?"));
2108 free_chain ((GstPlayChain *) chain);
2113 post_missing_element_message (playsink, "audioresample");
2114 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2115 (_("Missing element '%s' - check your GStreamer installation."),
2116 "audioresample"), (NULL));
2117 free_chain ((GstPlayChain *) chain);
2122 post_missing_element_message (playsink, "goom");
2123 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2124 (_("Missing element '%s' - check your GStreamer installation."),
2126 free_chain ((GstPlayChain *) chain);
2131 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2132 (NULL), ("Failed to configure the visualisation element."));
2133 /* element made it to READY */
2134 gst_element_set_state (chain->vis, GST_STATE_NULL);
2135 free_chain ((GstPlayChain *) chain);
2140 /* this function is called when all the request pads are requested and when we
2141 * have to construct the final pipeline. Based on the flags we construct the
2142 * final output pipelines.
2145 gst_play_sink_reconfigure (GstPlaySink * playsink)
2148 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2150 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2152 /* assume we need nothing */
2153 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2155 GST_PLAY_SINK_LOCK (playsink);
2156 GST_OBJECT_LOCK (playsink);
2157 /* get flags, there are protected with the object lock */
2158 flags = playsink->flags;
2159 GST_OBJECT_UNLOCK (playsink);
2161 /* figure out which components we need */
2162 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2163 /* we have subtitles and we are requested to show it */
2167 if (((flags & GST_PLAY_FLAG_VIDEO)
2168 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2169 /* we have video and we are requested to show it */
2172 /* we only deinterlace if native video is not requested and
2173 * we have raw video */
2174 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2175 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2176 need_deinterlace = TRUE;
2179 if (playsink->audio_pad) {
2180 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2183 if (playsink->audio_pad_raw) {
2184 /* only can do vis with raw uncompressed audio */
2185 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2186 /* also add video when we add visualisation */
2193 /* we have a text_pad and we need text rendering, in this case we need a
2194 * video_pad to combine the video with the text or visualizations */
2195 if (need_text && !need_video) {
2196 if (playsink->video_pad) {
2198 } else if (need_audio) {
2199 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2200 (_("Can't play a text file without video or visualizations.")),
2201 ("Have text pad but no video pad or visualizations"));
2204 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2205 (_("Can't play a text file without video or visualizations.")),
2206 ("Have text pad but no video pad or visualizations"));
2207 GST_PLAY_SINK_UNLOCK (playsink);
2212 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2213 need_video, need_vis, need_text);
2215 /* set up video pipeline */
2217 gboolean raw, async;
2219 /* we need a raw sink when we do vis or when we have a raw pad */
2220 raw = need_vis ? TRUE : playsink->video_pad_raw;
2221 /* we try to set the sink async=FALSE when we need vis, this way we can
2222 * avoid a queue in the audio chain. */
2225 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2226 playsink->video_pad_raw);
2228 if (playsink->videochain) {
2229 /* try to reactivate the chain */
2230 if (!setup_video_chain (playsink, raw, async)) {
2231 if (playsink->video_sinkpad_stream_synchronizer) {
2232 gst_element_release_request_pad (GST_ELEMENT_CAST
2233 (playsink->stream_synchronizer),
2234 playsink->video_sinkpad_stream_synchronizer);
2235 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2236 playsink->video_sinkpad_stream_synchronizer = NULL;
2237 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2238 playsink->video_srcpad_stream_synchronizer = NULL;
2241 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2243 /* Remove the sink from the bin to keep its state
2244 * and unparent it to allow reuse */
2245 if (playsink->videochain->sink)
2246 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2247 playsink->videochain->sink);
2249 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2250 free_chain ((GstPlayChain *) playsink->videochain);
2251 playsink->videochain = NULL;
2255 if (!playsink->videochain)
2256 playsink->videochain = gen_video_chain (playsink, raw, async);
2257 if (!playsink->videochain)
2260 if (!playsink->video_sinkpad_stream_synchronizer) {
2261 GValue item = { 0, };
2264 playsink->video_sinkpad_stream_synchronizer =
2265 gst_element_get_request_pad (GST_ELEMENT_CAST
2266 (playsink->stream_synchronizer), "sink_%u");
2267 it = gst_pad_iterate_internal_links
2268 (playsink->video_sinkpad_stream_synchronizer);
2270 gst_iterator_next (it, &item);
2271 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2272 g_value_unset (&item);
2273 g_assert (playsink->video_srcpad_stream_synchronizer);
2274 gst_iterator_free (it);
2277 if (playsink->video_pad)
2278 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2279 playsink->video_sinkpad_stream_synchronizer);
2281 if (need_deinterlace) {
2282 if (!playsink->videodeinterlacechain)
2283 playsink->videodeinterlacechain =
2284 gen_video_deinterlace_chain (playsink);
2285 if (!playsink->videodeinterlacechain)
2288 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2290 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2292 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2293 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2295 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2296 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2298 if (playsink->videodeinterlacechain) {
2299 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2300 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2305 GST_DEBUG_OBJECT (playsink, "adding video chain");
2306 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2307 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2308 /* if we are not part of vis or subtitles, set the ghostpad target */
2309 if (!need_vis && !need_text && (!playsink->textchain
2310 || !playsink->text_pad)) {
2311 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2312 if (need_deinterlace)
2313 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2314 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2316 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2317 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2320 GST_DEBUG_OBJECT (playsink, "no video needed");
2321 if (playsink->videochain) {
2322 GST_DEBUG_OBJECT (playsink, "removing video chain");
2323 if (playsink->vischain) {
2326 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2328 /* also had visualisation, release the tee srcpad before we then
2329 * unlink the video from it */
2330 if (playsink->audio_tee_vissrc) {
2331 gst_element_release_request_pad (playsink->audio_tee,
2332 playsink->audio_tee_vissrc);
2333 gst_object_unref (playsink->audio_tee_vissrc);
2334 playsink->audio_tee_vissrc = NULL;
2337 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2338 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2341 if (playsink->video_sinkpad_stream_synchronizer) {
2342 gst_element_release_request_pad (GST_ELEMENT_CAST
2343 (playsink->stream_synchronizer),
2344 playsink->video_sinkpad_stream_synchronizer);
2345 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2346 playsink->video_sinkpad_stream_synchronizer = NULL;
2347 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2348 playsink->video_srcpad_stream_synchronizer = NULL;
2351 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2352 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2353 if (playsink->videochain->ts_offset)
2354 gst_object_unref (playsink->videochain->ts_offset);
2355 playsink->videochain->ts_offset = NULL;
2358 if (playsink->videodeinterlacechain) {
2359 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2360 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2363 if (playsink->video_pad)
2364 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2370 GST_DEBUG_OBJECT (playsink, "adding audio");
2372 /* get a raw sink if we are asked for a raw pad */
2373 raw = playsink->audio_pad_raw;
2375 if (playsink->audiochain) {
2376 /* try to reactivate the chain */
2377 if (!setup_audio_chain (playsink, raw)) {
2378 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2379 if (playsink->audio_tee_asrc) {
2380 gst_element_release_request_pad (playsink->audio_tee,
2381 playsink->audio_tee_asrc);
2382 gst_object_unref (playsink->audio_tee_asrc);
2383 playsink->audio_tee_asrc = NULL;
2386 if (playsink->audio_sinkpad_stream_synchronizer) {
2387 gst_element_release_request_pad (GST_ELEMENT_CAST
2388 (playsink->stream_synchronizer),
2389 playsink->audio_sinkpad_stream_synchronizer);
2390 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2391 playsink->audio_sinkpad_stream_synchronizer = NULL;
2392 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2393 playsink->audio_srcpad_stream_synchronizer = NULL;
2396 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2398 /* Remove the sink from the bin to keep its state
2399 * and unparent it to allow reuse */
2400 if (playsink->audiochain->sink)
2401 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2402 playsink->audiochain->sink);
2404 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2405 disconnect_chain (playsink->audiochain, playsink);
2406 playsink->audiochain->volume = NULL;
2407 playsink->audiochain->mute = NULL;
2408 if (playsink->audiochain->ts_offset)
2409 gst_object_unref (playsink->audiochain->ts_offset);
2410 playsink->audiochain->ts_offset = NULL;
2411 free_chain ((GstPlayChain *) playsink->audiochain);
2412 playsink->audiochain = NULL;
2413 playsink->volume_changed = playsink->mute_changed = FALSE;
2417 if (!playsink->audiochain) {
2418 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2419 playsink->audiochain = gen_audio_chain (playsink, raw);
2422 if (!playsink->audio_sinkpad_stream_synchronizer) {
2423 GValue item = { 0, };
2426 playsink->audio_sinkpad_stream_synchronizer =
2427 gst_element_get_request_pad (GST_ELEMENT_CAST
2428 (playsink->stream_synchronizer), "sink_%u");
2429 it = gst_pad_iterate_internal_links
2430 (playsink->audio_sinkpad_stream_synchronizer);
2432 gst_iterator_next (it, &item);
2433 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
2434 g_value_unset (&item);
2435 g_assert (playsink->audio_srcpad_stream_synchronizer);
2436 gst_iterator_free (it);
2439 if (playsink->audiochain) {
2440 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2441 if (playsink->audio_tee_asrc == NULL) {
2442 playsink->audio_tee_asrc =
2443 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2445 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2446 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2447 gst_pad_link_full (playsink->audio_tee_asrc,
2448 playsink->audio_sinkpad_stream_synchronizer,
2449 GST_PAD_LINK_CHECK_NOTHING);
2450 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2451 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2454 GST_DEBUG_OBJECT (playsink, "no audio needed");
2455 /* we have no audio or we are requested to not play audio */
2456 if (playsink->audiochain) {
2457 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2458 /* release the audio pad */
2459 if (playsink->audio_tee_asrc) {
2460 gst_element_release_request_pad (playsink->audio_tee,
2461 playsink->audio_tee_asrc);
2462 gst_object_unref (playsink->audio_tee_asrc);
2463 playsink->audio_tee_asrc = NULL;
2466 if (playsink->audio_sinkpad_stream_synchronizer) {
2467 gst_element_release_request_pad (GST_ELEMENT_CAST
2468 (playsink->stream_synchronizer),
2469 playsink->audio_sinkpad_stream_synchronizer);
2470 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2471 playsink->audio_sinkpad_stream_synchronizer = NULL;
2472 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2473 playsink->audio_srcpad_stream_synchronizer = NULL;
2476 if (playsink->audiochain->sink_volume) {
2477 disconnect_chain (playsink->audiochain, playsink);
2478 playsink->audiochain->volume = NULL;
2479 playsink->audiochain->mute = NULL;
2480 if (playsink->audiochain->ts_offset)
2481 gst_object_unref (playsink->audiochain->ts_offset);
2482 playsink->audiochain->ts_offset = NULL;
2484 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2485 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2492 if (!playsink->vischain)
2493 playsink->vischain = gen_vis_chain (playsink);
2495 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2497 if (playsink->vischain) {
2498 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2500 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2501 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2502 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2503 if (playsink->audio_tee_vissrc == NULL) {
2504 playsink->audio_tee_vissrc =
2505 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2507 gst_pad_link_full (playsink->audio_tee_vissrc,
2508 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2509 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2510 GST_PAD_LINK_CHECK_NOTHING);
2511 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2512 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2513 gst_object_unref (srcpad);
2516 GST_DEBUG_OBJECT (playsink, "no vis needed");
2517 if (playsink->vischain) {
2518 if (playsink->audio_tee_vissrc) {
2519 gst_element_release_request_pad (playsink->audio_tee,
2520 playsink->audio_tee_vissrc);
2521 gst_object_unref (playsink->audio_tee_vissrc);
2522 playsink->audio_tee_vissrc = NULL;
2524 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2525 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2526 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2531 GST_DEBUG_OBJECT (playsink, "adding text");
2532 if (!playsink->textchain) {
2533 GST_DEBUG_OBJECT (playsink, "creating text chain");
2534 playsink->textchain = gen_text_chain (playsink);
2536 if (playsink->textchain) {
2539 GST_DEBUG_OBJECT (playsink, "adding text chain");
2540 if (playsink->textchain->overlay)
2541 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2543 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2545 if (!playsink->text_sinkpad_stream_synchronizer) {
2546 GValue item = { 0, };
2548 playsink->text_sinkpad_stream_synchronizer =
2549 gst_element_get_request_pad (GST_ELEMENT_CAST
2550 (playsink->stream_synchronizer), "sink_%u");
2551 it = gst_pad_iterate_internal_links
2552 (playsink->text_sinkpad_stream_synchronizer);
2554 gst_iterator_next (it, &item);
2555 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
2556 g_value_unset (&item);
2557 g_assert (playsink->text_srcpad_stream_synchronizer);
2558 gst_iterator_free (it);
2560 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2561 playsink->text_sinkpad_stream_synchronizer);
2562 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2563 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2570 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2571 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2572 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2573 GST_PAD_LINK_CHECK_NOTHING);
2574 gst_object_unref (srcpad);
2576 if (need_deinterlace)
2577 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2578 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2580 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2581 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2583 gst_pad_link_full (playsink->textchain->srcpad,
2584 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2586 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2589 GST_DEBUG_OBJECT (playsink, "no text needed");
2590 /* we have no subtitles/text or we are requested to not show them */
2592 if (playsink->text_sinkpad_stream_synchronizer) {
2593 gst_element_release_request_pad (GST_ELEMENT_CAST
2594 (playsink->stream_synchronizer),
2595 playsink->text_sinkpad_stream_synchronizer);
2596 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2597 playsink->text_sinkpad_stream_synchronizer = NULL;
2598 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2599 playsink->text_srcpad_stream_synchronizer = NULL;
2602 if (playsink->textchain) {
2603 if (playsink->text_pad == NULL) {
2604 /* no text pad, remove the chain entirely */
2605 GST_DEBUG_OBJECT (playsink, "removing text chain");
2606 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2607 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2609 /* we have a chain and a textpad, turn the subtitles off */
2610 GST_DEBUG_OBJECT (playsink, "turning off the text");
2611 if (playsink->textchain->overlay)
2612 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2616 if (!need_video && playsink->video_pad) {
2617 if (playsink->video_sinkpad_stream_synchronizer) {
2618 gst_element_release_request_pad (GST_ELEMENT_CAST
2619 (playsink->stream_synchronizer),
2620 playsink->video_sinkpad_stream_synchronizer);
2621 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2622 playsink->video_sinkpad_stream_synchronizer = NULL;
2623 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2624 playsink->video_srcpad_stream_synchronizer = NULL;
2627 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2630 if (playsink->text_pad && !playsink->textchain)
2631 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2633 update_av_offset (playsink);
2634 do_async_done (playsink);
2635 GST_PLAY_SINK_UNLOCK (playsink);
2642 /* gen_ chain already posted error */
2643 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2644 GST_PLAY_SINK_UNLOCK (playsink);
2650 * gst_play_sink_set_flags:
2651 * @playsink: a #GstPlaySink
2652 * @flags: #GstPlayFlags
2654 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2655 * when constructing the sink pipelins.
2657 * Returns: TRUE if the flags could be configured.
2660 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2662 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2664 GST_OBJECT_LOCK (playsink);
2665 playsink->flags = flags;
2666 GST_OBJECT_UNLOCK (playsink);
2672 * gst_play_sink_get_flags:
2673 * @playsink: a #GstPlaySink
2675 * Get the flags of @playsink. That flags control the behaviour of the sink when
2676 * it constructs the sink pipelines.
2678 * Returns: the currently configured #GstPlayFlags.
2681 gst_play_sink_get_flags (GstPlaySink * playsink)
2685 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2687 GST_OBJECT_LOCK (playsink);
2688 res = playsink->flags;
2689 GST_OBJECT_UNLOCK (playsink);
2695 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2697 GstPlayTextChain *chain;
2699 GST_PLAY_SINK_LOCK (playsink);
2700 chain = (GstPlayTextChain *) playsink->textchain;
2701 g_free (playsink->font_desc);
2702 playsink->font_desc = g_strdup (desc);
2703 if (chain && chain->overlay) {
2704 g_object_set (chain->overlay, "font-desc", desc, NULL);
2706 GST_PLAY_SINK_UNLOCK (playsink);
2710 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2712 gchar *result = NULL;
2713 GstPlayTextChain *chain;
2715 GST_PLAY_SINK_LOCK (playsink);
2716 chain = (GstPlayTextChain *) playsink->textchain;
2717 if (chain && chain->overlay) {
2718 g_object_get (chain->overlay, "font-desc", &result, NULL);
2719 playsink->font_desc = g_strdup (result);
2721 result = g_strdup (playsink->font_desc);
2723 GST_PLAY_SINK_UNLOCK (playsink);
2729 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2730 const gchar * encoding)
2732 GstPlayTextChain *chain;
2734 GST_PLAY_SINK_LOCK (playsink);
2735 chain = (GstPlayTextChain *) playsink->textchain;
2736 g_free (playsink->subtitle_encoding);
2737 playsink->subtitle_encoding = g_strdup (encoding);
2738 if (chain && chain->overlay) {
2739 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2741 GST_PLAY_SINK_UNLOCK (playsink);
2745 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2747 gchar *result = NULL;
2748 GstPlayTextChain *chain;
2750 GST_PLAY_SINK_LOCK (playsink);
2751 chain = (GstPlayTextChain *) playsink->textchain;
2752 if (chain && chain->overlay) {
2753 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2754 playsink->subtitle_encoding = g_strdup (result);
2756 result = g_strdup (playsink->subtitle_encoding);
2758 GST_PLAY_SINK_UNLOCK (playsink);
2764 update_av_offset (GstPlaySink * playsink)
2767 GstPlayAudioChain *achain;
2768 GstPlayVideoChain *vchain;
2770 av_offset = playsink->av_offset;
2771 achain = (GstPlayAudioChain *) playsink->audiochain;
2772 vchain = (GstPlayVideoChain *) playsink->videochain;
2774 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2775 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2776 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2778 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2783 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2785 GST_PLAY_SINK_LOCK (playsink);
2786 playsink->av_offset = av_offset;
2787 update_av_offset (playsink);
2788 GST_PLAY_SINK_UNLOCK (playsink);
2792 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2796 GST_PLAY_SINK_LOCK (playsink);
2797 result = playsink->av_offset;
2798 GST_PLAY_SINK_UNLOCK (playsink);
2804 * gst_play_sink_get_last_sample:
2805 * @playsink: a #GstPlaySink
2807 * Get the last displayed sample from @playsink. This sample is in the native
2808 * format of the sink element, the caps in the result sample contain the format
2809 * of the frame data.
2811 * Returns: a #GstSample with the frame data or %NULL when no video frame is
2815 gst_play_sink_get_last_sample (GstPlaySink * playsink)
2817 GstSample *result = NULL;
2818 GstPlayVideoChain *chain;
2820 GST_PLAY_SINK_LOCK (playsink);
2821 GST_DEBUG_OBJECT (playsink, "taking last sample");
2822 /* get the video chain if we can */
2823 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2824 GST_DEBUG_OBJECT (playsink, "found video chain");
2825 /* see if the chain is active */
2826 if (chain->chain.activated && chain->sink) {
2829 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2831 /* find and get the last-buffer property now */
2833 gst_play_sink_find_property (playsink, chain->sink,
2834 "last-sample", GST_TYPE_SAMPLE))) {
2835 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
2836 g_object_get (elem, "last-sample", &result, NULL);
2837 gst_object_unref (elem);
2841 GST_PLAY_SINK_UNLOCK (playsink);
2847 * gst_play_sink_convert_sample:
2848 * @playsink: a #GstPlaySink
2851 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2852 * be in the native format of the sink element and the caps on the buffer
2853 * describe the format of the frame. If @caps is not %NULL, the video
2854 * frame will be converted to the format of the caps.
2856 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2857 * available or when the conversion failed.
2860 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
2865 result = gst_play_sink_get_last_sample (playsink);
2866 if (result != NULL && caps != NULL) {
2869 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
2870 if (temp == NULL && err)
2873 gst_sample_unref (result);
2881 /* I'm really uncertain whether we should make playsink post an error
2882 * on the bus or not. It's not like it's a critical issue regarding
2883 * playsink behaviour. */
2884 GST_ERROR ("Error converting frame: %s", err->message);
2885 gst_sample_unref (result);
2892 is_raw_structure (GstStructure * s)
2896 name = gst_structure_get_name (s);
2898 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
2904 is_raw_pad (GstPad * pad)
2906 GstPad *peer = gst_pad_get_peer (pad);
2908 gboolean raw = TRUE;
2913 caps = gst_pad_get_current_caps (peer);
2917 caps = gst_pad_query_caps (peer, NULL);
2919 n = gst_caps_get_size (caps);
2920 for (i = 0; i < n; i++) {
2921 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2925 } else if (raw != r) {
2926 GST_ERROR_OBJECT (pad,
2927 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2933 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2935 gst_caps_unref (caps);
2936 gst_object_unref (peer);
2941 static GstPadProbeReturn
2942 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
2943 gpointer user_data);
2946 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
2948 if (playsink->video_pad) {
2950 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2951 (playsink->video_pad)));
2952 if (blocked && playsink->video_block_id == 0) {
2953 playsink->video_block_id =
2954 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
2955 sinkpad_blocked_cb, gst_object_ref (playsink),
2956 (GDestroyNotify) gst_object_unref);
2957 } else if (!blocked && playsink->video_block_id) {
2958 gst_pad_remove_probe (opad, playsink->video_block_id);
2959 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2960 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2961 playsink->video_block_id = 0;
2962 playsink->video_pad_blocked = FALSE;
2964 gst_object_unref (opad);
2969 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
2971 if (playsink->audio_pad) {
2973 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2974 (playsink->audio_pad)));
2975 if (blocked && playsink->audio_block_id == 0) {
2976 playsink->audio_block_id =
2977 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
2978 sinkpad_blocked_cb, gst_object_ref (playsink),
2979 (GDestroyNotify) gst_object_unref);
2980 } else if (!blocked && playsink->audio_block_id) {
2981 gst_pad_remove_probe (opad, playsink->audio_block_id);
2982 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2983 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2984 playsink->audio_block_id = 0;
2985 playsink->audio_pad_blocked = FALSE;
2987 gst_object_unref (opad);
2992 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
2994 if (playsink->text_pad) {
2996 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2997 (playsink->text_pad)));
2998 if (blocked && playsink->text_block_id == 0) {
2999 playsink->text_block_id =
3000 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3001 sinkpad_blocked_cb, gst_object_ref (playsink),
3002 (GDestroyNotify) gst_object_unref);
3003 } else if (!blocked && playsink->text_block_id) {
3004 gst_pad_remove_probe (opad, playsink->text_block_id);
3005 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3006 playsink->text_block_id = 0;
3007 playsink->text_pad_blocked = FALSE;
3009 gst_object_unref (opad);
3013 static GstPadProbeReturn
3014 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3017 GstPlaySink *playsink = (GstPlaySink *) user_data;
3020 GST_PLAY_SINK_LOCK (playsink);
3022 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3023 if (pad == playsink->video_pad) {
3024 playsink->video_pad_blocked = TRUE;
3025 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3026 } else if (pad == playsink->audio_pad) {
3027 playsink->audio_pad_blocked = TRUE;
3028 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3029 } else if (pad == playsink->text_pad) {
3030 playsink->text_pad_blocked = TRUE;
3031 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3034 /* We reconfigure when for ALL streams:
3035 * * there isn't a pad
3036 * * OR the pad is blocked
3037 * * OR there are no pending blocks on that pad
3040 if ((!playsink->video_pad || playsink->video_pad_blocked
3041 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3042 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3043 && (!playsink->text_pad || playsink->text_pad_blocked
3044 || !PENDING_TEXT_BLOCK (playsink))) {
3045 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3047 if (playsink->video_pad) {
3048 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3049 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3050 playsink->video_pad_raw);
3053 if (playsink->audio_pad) {
3054 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3055 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3056 playsink->audio_pad_raw);
3059 gst_play_sink_reconfigure (playsink);
3061 video_set_blocked (playsink, FALSE);
3062 audio_set_blocked (playsink, FALSE);
3063 text_set_blocked (playsink, FALSE);
3066 gst_object_unref (pad);
3068 GST_PLAY_SINK_UNLOCK (playsink);
3070 return GST_PAD_PROBE_OK;
3074 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3076 gboolean reconfigure = FALSE;
3080 g_object_get (pad, "caps", &caps, NULL);
3084 if (pad == playsink->audio_pad) {
3085 raw = is_raw_pad (pad);
3086 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3087 && playsink->audiochain;
3088 GST_DEBUG_OBJECT (pad,
3089 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3091 } else if (pad == playsink->video_pad) {
3092 raw = is_raw_pad (pad);
3093 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3094 && playsink->videochain;
3095 GST_DEBUG_OBJECT (pad,
3096 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3100 gst_caps_unref (caps);
3103 GST_PLAY_SINK_LOCK (playsink);
3104 video_set_blocked (playsink, TRUE);
3105 audio_set_blocked (playsink, TRUE);
3106 text_set_blocked (playsink, TRUE);
3107 GST_PLAY_SINK_UNLOCK (playsink);
3112 * gst_play_sink_request_pad
3113 * @playsink: a #GstPlaySink
3114 * @type: a #GstPlaySinkType
3116 * Create or return a pad of @type.
3118 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3121 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3124 gboolean created = FALSE;
3125 gboolean activate = TRUE;
3126 const gchar *pad_name = NULL;
3127 gulong *block_id = NULL;
3129 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3131 GST_PLAY_SINK_LOCK (playsink);
3133 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3134 case GST_PLAY_SINK_TYPE_AUDIO:
3135 pad_name = "audio_sink";
3136 if (!playsink->audio_tee) {
3137 GST_LOG_OBJECT (playsink, "creating tee");
3138 /* create tee when needed. This element will feed the audio sink chain
3139 * and the vis chain. */
3140 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3141 if (playsink->audio_tee == NULL) {
3142 post_missing_element_message (playsink, "tee");
3143 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3144 (_("Missing element '%s' - check your GStreamer installation."),
3149 playsink->audio_tee_sink =
3150 gst_element_get_static_pad (playsink->audio_tee, "sink");
3151 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3152 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3155 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3157 if (!playsink->audio_pad) {
3158 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3159 playsink->audio_pad =
3160 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3161 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3162 G_CALLBACK (caps_notify_cb), playsink);
3165 playsink->audio_pad_raw = FALSE;
3166 res = playsink->audio_pad;
3167 block_id = &playsink->audio_block_id;
3169 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3170 case GST_PLAY_SINK_TYPE_VIDEO:
3171 pad_name = "video_sink";
3172 if (!playsink->video_pad) {
3173 GST_LOG_OBJECT (playsink, "ghosting videosink");
3174 playsink->video_pad =
3175 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3176 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3177 G_CALLBACK (caps_notify_cb), playsink);
3180 playsink->video_pad_raw = FALSE;
3181 res = playsink->video_pad;
3182 block_id = &playsink->video_block_id;
3184 case GST_PLAY_SINK_TYPE_TEXT:
3185 GST_LOG_OBJECT (playsink, "ghosting text");
3186 if (!playsink->text_pad) {
3187 playsink->text_pad =
3188 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3191 res = playsink->text_pad;
3192 block_id = &playsink->text_block_id;
3194 case GST_PLAY_SINK_TYPE_FLUSHING:
3198 /* we need a unique padname for the flushing pad. */
3199 padname = g_strdup_printf ("flushing_%u", playsink->count);
3200 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3211 GST_PLAY_SINK_UNLOCK (playsink);
3213 if (created && res) {
3214 /* we have to add the pad when it's active or we get an error when the
3215 * element is 'running' */
3216 gst_pad_set_active (res, TRUE);
3217 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3218 if (block_id && *block_id == 0) {
3220 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3223 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3224 sinkpad_blocked_cb, gst_object_ref (playsink),
3225 (GDestroyNotify) gst_object_unref);
3226 PENDING_FLAG_SET (playsink, type);
3227 gst_object_unref (blockpad);
3230 gst_pad_set_active (res, activate);
3237 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3238 const gchar * name, const GstCaps * caps)
3242 GstPlaySinkType type;
3243 const gchar *tplname;
3245 g_return_val_if_fail (templ != NULL, NULL);
3247 GST_DEBUG_OBJECT (element, "name:%s", name);
3249 psink = GST_PLAY_SINK (element);
3250 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3252 /* Figure out the GstPlaySinkType based on the template */
3253 if (!strcmp (tplname, "audio_sink"))
3254 type = GST_PLAY_SINK_TYPE_AUDIO;
3255 else if (!strcmp (tplname, "audio_raw_sink"))
3256 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3257 else if (!strcmp (tplname, "video_sink"))
3258 type = GST_PLAY_SINK_TYPE_VIDEO;
3259 else if (!strcmp (tplname, "video_raw_sink"))
3260 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3261 else if (!strcmp (tplname, "text_sink"))
3262 type = GST_PLAY_SINK_TYPE_TEXT;
3264 goto unknown_template;
3266 pad = gst_play_sink_request_pad (psink, type);
3270 GST_WARNING_OBJECT (element, "Unknown pad template");
3275 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3277 GstPad **res = NULL;
3278 gboolean untarget = TRUE;
3280 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3282 GST_PLAY_SINK_LOCK (playsink);
3283 if (pad == playsink->video_pad) {
3284 res = &playsink->video_pad;
3285 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3287 } else if (pad == playsink->audio_pad) {
3288 res = &playsink->audio_pad;
3289 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3291 } else if (pad == playsink->text_pad) {
3292 res = &playsink->text_pad;
3294 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3298 GST_PLAY_SINK_UNLOCK (playsink);
3301 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3302 gst_pad_set_active (*res, FALSE);
3304 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3305 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3307 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3308 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3314 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3316 GstPlaySink *psink = GST_PLAY_SINK (element);
3318 gst_play_sink_release_pad (psink, pad);
3322 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3324 GstPlaySink *playsink;
3326 playsink = GST_PLAY_SINK_CAST (bin);
3328 switch (GST_MESSAGE_TYPE (message)) {
3329 case GST_MESSAGE_STEP_DONE:
3334 gboolean flush, intermediate, eos;
3337 GST_INFO_OBJECT (playsink, "Handling step-done message");
3338 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3339 &intermediate, &duration, &eos);
3341 if (format == GST_FORMAT_BUFFERS) {
3342 /* for the buffer format, we align the other streams */
3343 if (playsink->audiochain) {
3347 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3350 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3351 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3355 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3359 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3364 /* Send an event to our sinks until one of them works; don't then send to the
3365 * remaining sinks (unlike GstBin)
3366 * Special case: If a text sink is set we need to send the event
3367 * to them in case it's source is different from the a/v stream's source.
3370 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3372 gboolean res = TRUE;
3374 if (playsink->textchain && playsink->textchain->sink) {
3375 gst_event_ref (event);
3376 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3377 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3379 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3383 if (playsink->videochain) {
3384 gst_event_ref (event);
3385 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3386 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3389 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3391 if (playsink->audiochain) {
3392 gst_event_ref (event);
3393 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3394 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3397 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3401 gst_event_unref (event);
3405 /* We only want to send the event to a single sink (overriding GstBin's
3406 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3407 * events appropriately. So, this is a messy duplication of code. */
3409 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3411 gboolean res = FALSE;
3412 GstEventType event_type = GST_EVENT_TYPE (event);
3413 GstPlaySink *playsink;
3415 playsink = GST_PLAY_SINK_CAST (element);
3417 switch (event_type) {
3418 case GST_EVENT_SEEK:
3419 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3420 res = gst_play_sink_send_event_to_sink (playsink, event);
3422 case GST_EVENT_STEP:
3427 gboolean flush, intermediate;
3429 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3432 if (format == GST_FORMAT_BUFFERS) {
3433 /* for buffers, we will try to step video frames, for other formats we
3434 * send the step to all sinks */
3435 res = gst_play_sink_send_event_to_sink (playsink, event);
3438 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3445 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3452 static GstStateChangeReturn
3453 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3455 GstStateChangeReturn ret;
3456 GstStateChangeReturn bret;
3458 GstPlaySink *playsink;
3460 playsink = GST_PLAY_SINK (element);
3462 switch (transition) {
3463 case GST_STATE_CHANGE_READY_TO_PAUSED:
3464 playsink->need_async_start = TRUE;
3465 /* we want to go async to PAUSED until we managed to configure and add the
3467 do_async_start (playsink);
3468 ret = GST_STATE_CHANGE_ASYNC;
3470 case GST_STATE_CHANGE_PAUSED_TO_READY:
3471 /* unblock all pads here */
3472 GST_PLAY_SINK_LOCK (playsink);
3473 video_set_blocked (playsink, FALSE);
3474 audio_set_blocked (playsink, FALSE);
3475 text_set_blocked (playsink, FALSE);
3476 GST_PLAY_SINK_UNLOCK (playsink);
3478 case GST_STATE_CHANGE_READY_TO_NULL:
3479 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3480 /* remove our links to the mute and volume elements when they were
3481 * provided by a sink */
3482 disconnect_chain (playsink->audiochain, playsink);
3483 playsink->audiochain->volume = NULL;
3484 playsink->audiochain->mute = NULL;
3487 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3488 gst_object_unref (playsink->audiochain->ts_offset);
3489 playsink->audiochain->ts_offset = NULL;
3492 if (playsink->videochain && playsink->videochain->ts_offset) {
3493 gst_object_unref (playsink->videochain->ts_offset);
3494 playsink->videochain->ts_offset = NULL;
3496 ret = GST_STATE_CHANGE_SUCCESS;
3499 /* all other state changes return SUCCESS by default, this value can be
3500 * overridden by the result of the children */
3501 ret = GST_STATE_CHANGE_SUCCESS;
3505 /* do the state change of the children */
3507 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3509 /* now look at the result of our children and adjust the return value */
3511 case GST_STATE_CHANGE_FAILURE:
3512 /* failure, we stop */
3513 goto activate_failed;
3514 case GST_STATE_CHANGE_NO_PREROLL:
3515 /* some child returned NO_PREROLL. This is strange but we never know. We
3516 * commit our async state change (if any) and return the NO_PREROLL */
3517 do_async_done (playsink);
3520 case GST_STATE_CHANGE_ASYNC:
3521 /* some child was async, return this */
3525 /* return our previously configured return value */
3529 switch (transition) {
3530 case GST_STATE_CHANGE_READY_TO_PAUSED:
3532 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3533 /* FIXME Release audio device when we implement that */
3534 playsink->need_async_start = TRUE;
3536 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3537 if (playsink->video_sinkpad_stream_synchronizer) {
3538 gst_element_release_request_pad (GST_ELEMENT_CAST
3539 (playsink->stream_synchronizer),
3540 playsink->video_sinkpad_stream_synchronizer);
3541 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3542 playsink->video_sinkpad_stream_synchronizer = NULL;
3543 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3544 playsink->video_srcpad_stream_synchronizer = NULL;
3546 if (playsink->audio_sinkpad_stream_synchronizer) {
3547 gst_element_release_request_pad (GST_ELEMENT_CAST
3548 (playsink->stream_synchronizer),
3549 playsink->audio_sinkpad_stream_synchronizer);
3550 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3551 playsink->audio_sinkpad_stream_synchronizer = NULL;
3552 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3553 playsink->audio_srcpad_stream_synchronizer = NULL;
3555 if (playsink->text_sinkpad_stream_synchronizer) {
3556 gst_element_release_request_pad (GST_ELEMENT_CAST
3557 (playsink->stream_synchronizer),
3558 playsink->text_sinkpad_stream_synchronizer);
3559 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3560 playsink->text_sinkpad_stream_synchronizer = NULL;
3561 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3562 playsink->text_srcpad_stream_synchronizer = NULL;
3566 case GST_STATE_CHANGE_READY_TO_NULL:
3567 /* remove sinks we added */
3568 if (playsink->videodeinterlacechain) {
3569 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3571 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3573 if (playsink->videochain) {
3574 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3575 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3577 if (playsink->audiochain) {
3578 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3579 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3581 if (playsink->vischain) {
3582 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3583 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3585 if (playsink->textchain) {
3586 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3587 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3589 do_async_done (playsink);
3590 /* when going to READY, keep elements around as long as possible,
3591 * so they may be re-used faster next time/url around.
3592 * when really going to NULL, clean up everything completely. */
3593 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3595 /* Unparent the sinks to allow reuse */
3596 if (playsink->videochain && playsink->videochain->sink)
3597 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3598 playsink->videochain->sink);
3599 if (playsink->audiochain && playsink->audiochain->sink)
3600 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3601 playsink->audiochain->sink);
3602 if (playsink->textchain && playsink->textchain->sink)
3603 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3604 playsink->textchain->sink);
3606 if (playsink->audio_sink != NULL)
3607 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3608 if (playsink->video_sink != NULL)
3609 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3610 if (playsink->visualisation != NULL)
3611 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3612 if (playsink->text_sink != NULL)
3613 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3615 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3616 playsink->videodeinterlacechain = NULL;
3617 free_chain ((GstPlayChain *) playsink->videochain);
3618 playsink->videochain = NULL;
3619 free_chain ((GstPlayChain *) playsink->audiochain);
3620 playsink->audiochain = NULL;
3621 free_chain ((GstPlayChain *) playsink->vischain);
3622 playsink->vischain = NULL;
3623 free_chain ((GstPlayChain *) playsink->textchain);
3624 playsink->textchain = NULL;
3635 GST_DEBUG_OBJECT (element,
3636 "element failed to change states -- activation problem?");
3637 return GST_STATE_CHANGE_FAILURE;
3642 gst_play_sink_set_property (GObject * object, guint prop_id,
3643 const GValue * value, GParamSpec * spec)
3645 GstPlaySink *playsink = GST_PLAY_SINK (object);
3649 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3652 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3655 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3657 case PROP_FONT_DESC:
3658 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3660 case PROP_SUBTITLE_ENCODING:
3661 gst_play_sink_set_subtitle_encoding (playsink,
3662 g_value_get_string (value));
3664 case PROP_VIS_PLUGIN:
3665 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3667 case PROP_AV_OFFSET:
3668 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3670 case PROP_VIDEO_SINK:
3671 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3672 g_value_get_object (value));
3674 case PROP_AUDIO_SINK:
3675 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3676 g_value_get_object (value));
3678 case PROP_TEXT_SINK:
3679 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3680 g_value_get_object (value));
3683 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3689 gst_play_sink_get_property (GObject * object, guint prop_id,
3690 GValue * value, GParamSpec * spec)
3692 GstPlaySink *playsink = GST_PLAY_SINK (object);
3696 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3699 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3702 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3704 case PROP_FONT_DESC:
3705 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3707 case PROP_SUBTITLE_ENCODING:
3708 g_value_take_string (value,
3709 gst_play_sink_get_subtitle_encoding (playsink));
3711 case PROP_VIS_PLUGIN:
3712 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3715 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
3717 case PROP_AV_OFFSET:
3718 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3720 case PROP_VIDEO_SINK:
3721 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3722 GST_PLAY_SINK_TYPE_VIDEO));
3724 case PROP_AUDIO_SINK:
3725 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3726 GST_PLAY_SINK_TYPE_AUDIO));
3728 case PROP_TEXT_SINK:
3729 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3730 GST_PLAY_SINK_TYPE_TEXT));
3733 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3740 gst_play_sink_plugin_init (GstPlugin * plugin)
3742 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3744 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3745 GST_TYPE_PLAY_SINK);