2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
26 * with newer GLib versions (>= 2.31.0) */
27 #define GLIB_DISABLE_DEPRECATION_WARNINGS
32 #include <gst/gst-i18n-plugin.h>
33 #include <gst/pbutils/pbutils.h>
34 #include <gst/video/video.h>
35 #include <gst/interfaces/streamvolume.h>
37 #include "gstplaysink.h"
38 #include "gststreamsynchronizer.h"
39 #include "gstplaysinkvideoconvert.h"
40 #include "gstplaysinkaudioconvert.h"
42 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
43 #define GST_CAT_DEFAULT gst_play_sink_debug
45 #define VOLUME_MAX_DOUBLE 10.0
47 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
48 GST_PLAY_FLAG_SOFT_VOLUME
50 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
52 /* holds the common data fields for the audio and video pipelines. We keep them
53 * in a structure to more easily have all the info available. */
56 GstPlaySink *playsink;
69 GstElement *volume; /* element with the volume property */
70 gboolean sink_volume; /* if the volume was provided by the sink */
71 GstElement *mute; /* element with the mute property */
73 GstElement *ts_offset;
79 GstPad *sinkpad, *srcpad;
81 GstElement *deinterlace;
82 } GstPlayVideoDeinterlaceChain;
92 GstElement *ts_offset;
101 GstElement *resample;
102 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
103 GstPad *vissinkpad; /* visualisation sinkpad, */
105 GstPad *vissrcpad; /* visualisation srcpad, */
106 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
115 GstElement *identity;
117 GstPad *videosinkpad;
119 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
121 GstElement *sink; /* custom sink to receive subtitle buffers */
124 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
125 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
126 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
127 g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
128 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
130 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
131 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
132 g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
135 #define PENDING_FLAG_SET(playsink, flagtype) \
136 ((playsink->pending_blocked_pads) |= (1 << flagtype))
137 #define PENDING_FLAG_UNSET(playsink, flagtype) \
138 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
139 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
140 ((playsink->pending_blocked_pads) & (1 << flagtype))
141 #define PENDING_VIDEO_BLOCK(playsink) \
142 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
143 #define PENDING_AUDIO_BLOCK(playsink) \
144 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
145 #define PENDING_TEXT_BLOCK(playsink) \
146 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
152 GStaticRecMutex lock;
154 gboolean async_pending;
155 gboolean need_async_start;
159 GstStreamSynchronizer *stream_synchronizer;
162 GstPlayAudioChain *audiochain;
163 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
164 GstPlayVideoChain *videochain;
165 GstPlayVisChain *vischain;
166 GstPlayTextChain *textchain;
170 gboolean audio_pad_raw;
171 gboolean audio_pad_blocked;
172 GstPad *audio_srcpad_stream_synchronizer;
173 GstPad *audio_sinkpad_stream_synchronizer;
175 GstElement *audio_tee;
176 GstPad *audio_tee_sink;
177 GstPad *audio_tee_asrc;
178 GstPad *audio_tee_vissrc;
181 gboolean video_pad_raw;
182 gboolean video_pad_blocked;
183 GstPad *video_srcpad_stream_synchronizer;
184 GstPad *video_sinkpad_stream_synchronizer;
187 gboolean text_pad_blocked;
188 GstPad *text_srcpad_stream_synchronizer;
189 GstPad *text_sinkpad_stream_synchronizer;
191 guint32 pending_blocked_pads;
194 GstElement *audio_sink;
195 GstElement *video_sink;
196 GstElement *visualisation;
197 GstElement *text_sink;
200 gchar *font_desc; /* font description */
201 gchar *subtitle_encoding; /* subtitle encoding */
202 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
204 gboolean volume_changed; /* volume/mute changed while no audiochain */
205 gboolean mute_changed; /* ... has been created yet */
209 struct _GstPlaySinkClass
211 GstBinClass parent_class;
213 gboolean (*reconfigure) (GstPlaySink * playsink);
215 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
219 static GstStaticPadTemplate audiotemplate =
220 GST_STATIC_PAD_TEMPLATE ("audio_sink",
223 GST_STATIC_CAPS_ANY);
224 static GstStaticPadTemplate videotemplate =
225 GST_STATIC_PAD_TEMPLATE ("video_sink",
228 GST_STATIC_CAPS_ANY);
229 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
232 GST_STATIC_CAPS_ANY);
234 /* FIXME 0.11: Remove */
235 static GstStaticPadTemplate audiorawtemplate =
236 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
239 GST_STATIC_CAPS_ANY);
240 static GstStaticPadTemplate videorawtemplate =
241 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
244 GST_STATIC_CAPS_ANY);
255 PROP_SUBTITLE_ENCODING,
271 static void gst_play_sink_dispose (GObject * object);
272 static void gst_play_sink_finalize (GObject * object);
273 static void gst_play_sink_set_property (GObject * object, guint prop_id,
274 const GValue * value, GParamSpec * spec);
275 static void gst_play_sink_get_property (GObject * object, guint prop_id,
276 GValue * value, GParamSpec * spec);
278 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
279 GstPadTemplate * templ, const gchar * name);
280 static void gst_play_sink_release_request_pad (GstElement * element,
282 static gboolean gst_play_sink_send_event (GstElement * element,
284 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
285 GstStateChange transition);
287 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
289 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
290 GstPlaySink * playsink);
291 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
292 GstPlaySink * playsink);
294 static void update_av_offset (GstPlaySink * playsink);
297 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
298 GValue * return_value G_GNUC_UNUSED,
299 guint n_param_values,
300 const GValue * param_values,
301 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
303 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
304 gpointer arg_1, gpointer data2);
305 register GMarshalFunc_OBJECT__BOXED callback;
306 register GCClosure *cc = (GCClosure *) closure;
307 register gpointer data1, data2;
309 g_return_if_fail (return_value != NULL);
310 g_return_if_fail (n_param_values == 2);
312 if (G_CCLOSURE_SWAP_DATA (closure)) {
313 data1 = closure->data;
314 data2 = g_value_peek_pointer (param_values + 0);
316 data1 = g_value_peek_pointer (param_values + 0);
317 data2 = closure->data;
320 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
322 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
324 gst_value_take_buffer (return_value, v_return);
327 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
330 _do_init (GType type)
332 static const GInterfaceInfo svol_info = {
336 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
339 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
340 _do_init (g_define_type_id));
343 gst_play_sink_class_init (GstPlaySinkClass * klass)
345 GObjectClass *gobject_klass;
346 GstElementClass *gstelement_klass;
347 GstBinClass *gstbin_klass;
349 gobject_klass = (GObjectClass *) klass;
350 gstelement_klass = (GstElementClass *) klass;
351 gstbin_klass = (GstBinClass *) klass;
353 gobject_klass->dispose = gst_play_sink_dispose;
354 gobject_klass->finalize = gst_play_sink_finalize;
355 gobject_klass->set_property = gst_play_sink_set_property;
356 gobject_klass->get_property = gst_play_sink_get_property;
362 * Control the behaviour of playsink.
364 g_object_class_install_property (gobject_klass, PROP_FLAGS,
365 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
366 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
367 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
370 * GstPlaySink:volume:
372 * Get or set the current audio stream volume. 1.0 means 100%,
373 * 0.0 means mute. This uses a linear volume scale.
376 g_object_class_install_property (gobject_klass, PROP_VOLUME,
377 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
378 0.0, VOLUME_MAX_DOUBLE, 1.0,
379 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
380 g_object_class_install_property (gobject_klass, PROP_MUTE,
381 g_param_spec_boolean ("mute", "Mute",
382 "Mute the audio channel without changing the volume", FALSE,
383 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
384 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
385 g_param_spec_string ("subtitle-font-desc",
386 "Subtitle font description",
387 "Pango font description of font "
388 "to be used for subtitle rendering", NULL,
389 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
390 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
391 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
392 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
393 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
394 "be checked for an encoding to use. If that is not set either, "
395 "ISO-8859-15 will be assumed.", NULL,
396 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
397 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
398 g_param_spec_object ("vis-plugin", "Vis plugin",
399 "the visualization element to use (NULL = default)",
400 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
404 * Get the currently rendered or prerolled frame in the video sink.
405 * The #GstCaps on the buffer will describe the format of the buffer.
409 g_object_class_install_property (gobject_klass, PROP_FRAME,
410 gst_param_spec_mini_object ("frame", "Frame",
411 "The last frame (NULL = no video available)",
412 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
414 * GstPlaySink:av-offset:
416 * Control the synchronisation offset between the audio and video streams.
417 * Positive values make the audio ahead of the video and negative values make
418 * the audio go behind the video.
422 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
423 g_param_spec_int64 ("av-offset", "AV Offset",
424 "The synchronisation offset between audio and video in nanoseconds",
425 G_MININT64, G_MAXINT64, 0,
426 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
429 * GstPlaySink:video-sink:
431 * Set the used video sink element. NULL will use the default sink. playsink
432 * must be in %GST_STATE_NULL
436 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
437 g_param_spec_object ("video-sink", "Video Sink",
438 "the video output element to use (NULL = default sink)",
439 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
441 * GstPlaySink:audio-sink:
443 * Set the used audio sink element. NULL will use the default sink. playsink
444 * must be in %GST_STATE_NULL
448 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
449 g_param_spec_object ("audio-sink", "Audio Sink",
450 "the audio output element to use (NULL = default sink)",
451 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
453 * GstPlaySink:text-sink:
455 * Set the used text sink element. NULL will use the default sink. playsink
456 * must be in %GST_STATE_NULL
460 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
461 g_param_spec_object ("text-sink", "Text sink",
462 "the text output element to use (NULL = default textoverlay)",
463 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
466 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
467 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
468 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
471 * GstPlaySink::convert-frame
472 * @playsink: a #GstPlaySink
473 * @caps: the target format of the frame
475 * Action signal to retrieve the currently playing video frame in the format
476 * specified by @caps.
477 * If @caps is %NULL, no conversion will be performed and this function is
478 * equivalent to the #GstPlaySink::frame property.
480 * Returns: a #GstBuffer of the current video frame converted to #caps.
481 * The caps on the buffer will describe the final layout of the buffer data.
482 * %NULL is returned when no current buffer can be retrieved or when the
487 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
488 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
489 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
490 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
492 gst_element_class_add_static_pad_template (gstelement_klass,
494 gst_element_class_add_static_pad_template (gstelement_klass, &audiotemplate);
495 gst_element_class_add_static_pad_template (gstelement_klass,
497 gst_element_class_add_static_pad_template (gstelement_klass, &videotemplate);
498 gst_element_class_add_static_pad_template (gstelement_klass, &texttemplate);
499 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
501 "Convenience sink for multiple streams",
502 "Wim Taymans <wim.taymans@gmail.com>");
504 gstelement_klass->change_state =
505 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
506 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
507 gstelement_klass->request_new_pad =
508 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
509 gstelement_klass->release_pad =
510 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
512 gstbin_klass->handle_message =
513 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
515 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
516 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
520 gst_play_sink_init (GstPlaySink * playsink)
523 playsink->video_sink = NULL;
524 playsink->audio_sink = NULL;
525 playsink->visualisation = NULL;
526 playsink->text_sink = NULL;
527 playsink->volume = 1.0;
528 playsink->font_desc = NULL;
529 playsink->subtitle_encoding = NULL;
530 playsink->flags = DEFAULT_FLAGS;
532 playsink->stream_synchronizer =
533 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
534 gst_bin_add (GST_BIN_CAST (playsink),
535 GST_ELEMENT_CAST (playsink->stream_synchronizer));
537 g_static_rec_mutex_init (&playsink->lock);
538 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
542 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
546 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
549 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
555 free_chain (GstPlayChain * chain)
559 gst_object_unref (chain->bin);
565 gst_play_sink_dispose (GObject * object)
567 GstPlaySink *playsink;
569 playsink = GST_PLAY_SINK (object);
571 if (playsink->audio_sink != NULL) {
572 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
573 gst_object_unref (playsink->audio_sink);
574 playsink->audio_sink = NULL;
576 if (playsink->video_sink != NULL) {
577 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
578 gst_object_unref (playsink->video_sink);
579 playsink->video_sink = NULL;
581 if (playsink->visualisation != NULL) {
582 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
583 gst_object_unref (playsink->visualisation);
584 playsink->visualisation = NULL;
586 if (playsink->text_sink != NULL) {
587 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
588 gst_object_unref (playsink->text_sink);
589 playsink->text_sink = NULL;
592 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
593 playsink->videodeinterlacechain = NULL;
594 free_chain ((GstPlayChain *) playsink->videochain);
595 playsink->videochain = NULL;
596 free_chain ((GstPlayChain *) playsink->audiochain);
597 playsink->audiochain = NULL;
598 free_chain ((GstPlayChain *) playsink->vischain);
599 playsink->vischain = NULL;
600 free_chain ((GstPlayChain *) playsink->textchain);
601 playsink->textchain = NULL;
603 if (playsink->audio_tee_sink) {
604 gst_object_unref (playsink->audio_tee_sink);
605 playsink->audio_tee_sink = NULL;
608 if (playsink->audio_tee_vissrc) {
609 gst_element_release_request_pad (playsink->audio_tee,
610 playsink->audio_tee_vissrc);
611 gst_object_unref (playsink->audio_tee_vissrc);
612 playsink->audio_tee_vissrc = NULL;
615 if (playsink->audio_tee_asrc) {
616 gst_element_release_request_pad (playsink->audio_tee,
617 playsink->audio_tee_asrc);
618 gst_object_unref (playsink->audio_tee_asrc);
619 playsink->audio_tee_asrc = NULL;
622 g_free (playsink->font_desc);
623 playsink->font_desc = NULL;
625 g_free (playsink->subtitle_encoding);
626 playsink->subtitle_encoding = NULL;
628 playsink->stream_synchronizer = NULL;
630 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
634 gst_play_sink_finalize (GObject * object)
636 GstPlaySink *playsink;
638 playsink = GST_PLAY_SINK (object);
640 g_static_rec_mutex_free (&playsink->lock);
642 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
646 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
649 GstElement **elem = NULL, *old = NULL;
651 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
653 GST_PLAY_SINK_LOCK (playsink);
655 case GST_PLAY_SINK_TYPE_AUDIO:
656 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
657 elem = &playsink->audio_sink;
659 case GST_PLAY_SINK_TYPE_VIDEO:
660 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
661 elem = &playsink->video_sink;
663 case GST_PLAY_SINK_TYPE_TEXT:
664 elem = &playsink->text_sink;
672 gst_object_ref (sink);
675 GST_PLAY_SINK_UNLOCK (playsink);
679 gst_element_set_state (old, GST_STATE_NULL);
680 gst_object_unref (old);
685 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
687 GstElement *result = NULL;
688 GstElement *elem = NULL, *chainp = NULL;
690 GST_PLAY_SINK_LOCK (playsink);
692 case GST_PLAY_SINK_TYPE_AUDIO:
693 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
695 GstPlayAudioChain *chain;
696 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
697 chainp = chain->sink;
698 elem = playsink->audio_sink;
701 case GST_PLAY_SINK_TYPE_VIDEO:
702 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
704 GstPlayVideoChain *chain;
705 if ((chain = (GstPlayVideoChain *) playsink->videochain))
706 chainp = chain->sink;
707 elem = playsink->video_sink;
710 case GST_PLAY_SINK_TYPE_TEXT:
712 GstPlayTextChain *chain;
713 if ((chain = (GstPlayTextChain *) playsink->textchain))
714 chainp = chain->sink;
715 elem = playsink->text_sink;
722 /* we have an active chain with a sink, get the sink */
723 result = gst_object_ref (chainp);
725 /* nothing found, return last configured sink */
726 if (result == NULL && elem)
727 result = gst_object_ref (elem);
728 GST_PLAY_SINK_UNLOCK (playsink);
734 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
737 GstPlaySink *playsink;
739 playsink = GST_PLAY_SINK (user_data);
740 /* nothing to do here, we need a dummy callback here to make the async call
742 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
746 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
749 GstPlaySink *playsink;
750 GstPlayVisChain *chain;
752 playsink = GST_PLAY_SINK (user_data);
754 GST_PLAY_SINK_LOCK (playsink);
755 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
756 /* now try to change the plugin in the running vis chain */
757 if (!(chain = (GstPlayVisChain *) playsink->vischain))
760 /* unlink the old plugin and unghost the pad */
761 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
762 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
764 /* set the old plugin to NULL and remove */
765 gst_element_set_state (chain->vis, GST_STATE_NULL);
766 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
768 /* add new plugin and set state to playing */
769 chain->vis = playsink->visualisation;
770 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
771 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
774 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
775 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
778 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
779 GST_PAD_LINK_CHECK_NOTHING);
780 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
784 /* Unblock the pad */
785 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
787 GST_PLAY_SINK_UNLOCK (playsink);
791 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
793 GstPlayVisChain *chain;
795 /* setting NULL means creating the default vis plugin */
797 vis = gst_element_factory_make ("goom", "vis");
799 /* simply return if we don't have a vis plugin here */
803 GST_PLAY_SINK_LOCK (playsink);
804 /* first store the new vis */
805 if (playsink->visualisation)
806 gst_object_unref (playsink->visualisation);
808 gst_object_ref_sink (vis);
809 playsink->visualisation = vis;
811 /* now try to change the plugin in the running vis chain, if we have no chain,
812 * we don't bother, any future vis chain will be created with the new vis
814 if (!(chain = (GstPlayVisChain *) playsink->vischain))
817 /* block the pad, the next time the callback is called we can change the
818 * visualisation. It's possible that this never happens or that the pad was
819 * already blocked. If the callback never happens, we don't have new data so
820 * we don't need the new vis plugin. If the pad was already blocked, the
821 * function returns FALSE but the previous pad block will do the right thing
823 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
824 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
827 GST_PLAY_SINK_UNLOCK (playsink);
833 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
835 GstElement *result = NULL;
836 GstPlayVisChain *chain;
838 GST_PLAY_SINK_LOCK (playsink);
839 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
840 /* we have an active chain, get the sink */
842 result = gst_object_ref (chain->vis);
844 /* nothing found, return last configured sink */
845 if (result == NULL && playsink->visualisation)
846 result = gst_object_ref (playsink->visualisation);
847 GST_PLAY_SINK_UNLOCK (playsink);
853 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
855 GstPlayAudioChain *chain;
857 GST_PLAY_SINK_LOCK (playsink);
858 playsink->volume = volume;
859 chain = (GstPlayAudioChain *) playsink->audiochain;
860 if (chain && chain->volume) {
861 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
862 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
863 chain->mute, volume, playsink->mute);
864 /* if there is a mute element or we are not muted, set the volume */
865 if (chain->mute || !playsink->mute)
866 g_object_set (chain->volume, "volume", volume, NULL);
868 GST_LOG_OBJECT (playsink, "no volume element");
869 playsink->volume_changed = TRUE;
871 GST_PLAY_SINK_UNLOCK (playsink);
875 gst_play_sink_get_volume (GstPlaySink * playsink)
878 GstPlayAudioChain *chain;
880 GST_PLAY_SINK_LOCK (playsink);
881 chain = (GstPlayAudioChain *) playsink->audiochain;
882 result = playsink->volume;
883 if (chain && chain->volume) {
884 if (chain->mute || !playsink->mute) {
885 g_object_get (chain->volume, "volume", &result, NULL);
886 playsink->volume = result;
889 GST_PLAY_SINK_UNLOCK (playsink);
895 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
897 GstPlayAudioChain *chain;
899 GST_PLAY_SINK_LOCK (playsink);
900 playsink->mute = mute;
901 chain = (GstPlayAudioChain *) playsink->audiochain;
904 g_object_set (chain->mute, "mute", mute, NULL);
905 } else if (chain->volume) {
907 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
909 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
913 playsink->mute_changed = TRUE;
915 GST_PLAY_SINK_UNLOCK (playsink);
919 gst_play_sink_get_mute (GstPlaySink * playsink)
922 GstPlayAudioChain *chain;
924 GST_PLAY_SINK_LOCK (playsink);
925 chain = (GstPlayAudioChain *) playsink->audiochain;
926 if (chain && chain->mute) {
927 g_object_get (chain->mute, "mute", &result, NULL);
928 playsink->mute = result;
930 result = playsink->mute;
932 GST_PLAY_SINK_UNLOCK (playsink);
938 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
942 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
943 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
947 add_chain (GstPlayChain * chain, gboolean add)
949 if (chain->added == add)
953 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
955 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
956 /* we don't want to lose our sink status */
957 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
966 activate_chain (GstPlayChain * chain, gboolean activate)
970 if (chain->activated == activate)
973 GST_OBJECT_LOCK (chain->playsink);
974 state = GST_STATE_TARGET (chain->playsink);
975 GST_OBJECT_UNLOCK (chain->playsink);
978 gst_element_set_state (chain->bin, state);
980 gst_element_set_state (chain->bin, GST_STATE_NULL);
982 chain->activated = activate;
988 element_is_sink (GstElement * element)
992 GST_OBJECT_LOCK (element);
993 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
994 GST_OBJECT_UNLOCK (element);
996 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1001 element_has_property (GstElement * element, const gchar * pname, GType type)
1005 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1007 if (pspec == NULL) {
1008 GST_DEBUG_OBJECT (element, "no %s property", pname);
1012 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1013 g_type_is_a (pspec->value_type, type)) {
1014 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1015 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1019 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1020 "and we expected it to be of type %s", pname,
1021 g_type_name (pspec->value_type), g_type_name (type));
1028 const gchar *prop_name;
1031 } FindPropertyHelper;
1034 find_property (GstElement * element, FindPropertyHelper * helper)
1036 if (helper->need_sink && !element_is_sink (element)) {
1037 gst_object_unref (element);
1041 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1042 gst_object_unref (element);
1046 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1047 (helper->need_sink) ? "sink" : "element");
1048 return 0; /* keep it */
1051 /* FIXME: why not move these functions into core? */
1052 /* find a sink in the hierarchy with a property named @name. This function does
1053 * not increase the refcount of the returned object and thus remains valid as
1054 * long as the bin is valid. */
1056 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1057 const gchar * name, GType expected_type)
1059 GstElement *result = NULL;
1062 if (element_has_property (obj, name, expected_type)) {
1064 } else if (GST_IS_BIN (obj)) {
1065 FindPropertyHelper helper = { name, expected_type, TRUE };
1067 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1068 result = gst_iterator_find_custom (it,
1069 (GCompareFunc) find_property, &helper);
1070 gst_iterator_free (it);
1071 /* we don't need the extra ref */
1073 gst_object_unref (result);
1078 /* find an object in the hierarchy with a property named @name */
1080 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1081 const gchar * name, GType expected_type)
1083 GstElement *result = NULL;
1086 if (GST_IS_BIN (obj)) {
1087 FindPropertyHelper helper = { name, expected_type, FALSE };
1089 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1090 result = gst_iterator_find_custom (it,
1091 (GCompareFunc) find_property, &helper);
1092 gst_iterator_free (it);
1094 if (element_has_property (obj, name, expected_type)) {
1096 gst_object_ref (obj);
1103 do_async_start (GstPlaySink * playsink)
1105 GstMessage *message;
1107 if (!playsink->need_async_start) {
1108 GST_INFO_OBJECT (playsink, "no async_start needed");
1112 playsink->async_pending = TRUE;
1114 GST_INFO_OBJECT (playsink, "Sending async_start message");
1115 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1116 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1117 (playsink), message);
1121 do_async_done (GstPlaySink * playsink)
1123 GstMessage *message;
1125 if (playsink->async_pending) {
1126 GST_INFO_OBJECT (playsink, "Sending async_done message");
1127 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1128 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1129 (playsink), message);
1131 playsink->async_pending = FALSE;
1134 playsink->need_async_start = FALSE;
1137 /* try to change the state of an element. This function returns the element when
1138 * the state change could be performed. When this function returns NULL an error
1139 * occured and the element is unreffed if @unref is TRUE. */
1141 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1143 GstStateChangeReturn ret;
1146 ret = gst_element_set_state (element, GST_STATE_READY);
1147 if (ret == GST_STATE_CHANGE_FAILURE) {
1148 GST_DEBUG_OBJECT (playsink, "failed state change..");
1149 gst_element_set_state (element, GST_STATE_NULL);
1151 gst_object_unref (element);
1158 /* make the element (bin) that contains the elements needed to perform
1159 * video display. Only used for *raw* video streams.
1161 * +------------------------------------------------------------+
1163 * | +-------+ +----------+ +----------+ +---------+ |
1164 * | | queue | |colorspace| |videoscale| |videosink| |
1165 * | +-sink src-sink src-sink src-sink | |
1166 * | | +-------+ +----------+ +----------+ +---------+ |
1168 * +------------------------------------------------------------+
1171 static GstPlayVideoDeinterlaceChain *
1172 gen_video_deinterlace_chain (GstPlaySink * playsink)
1174 GstPlayVideoDeinterlaceChain *chain;
1177 GstElement *head = NULL, *prev = NULL;
1179 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1180 chain->chain.playsink = playsink;
1182 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1184 /* create a bin to hold objects, as we create them we add them to this bin so
1185 * that when something goes wrong we only need to unref the bin */
1186 chain->chain.bin = gst_bin_new ("vdbin");
1187 bin = GST_BIN_CAST (chain->chain.bin);
1188 gst_object_ref_sink (bin);
1190 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1191 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1192 if (chain->conv == NULL) {
1193 post_missing_element_message (playsink, COLORSPACE);
1194 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1195 (_("Missing element '%s' - check your GStreamer installation."),
1196 COLORSPACE), ("video rendering might fail"));
1198 gst_bin_add (bin, chain->conv);
1203 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1204 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1205 if (chain->deinterlace == NULL) {
1206 post_missing_element_message (playsink, "deinterlace");
1207 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1208 (_("Missing element '%s' - check your GStreamer installation."),
1209 "deinterlace"), ("deinterlacing won't work"));
1211 gst_bin_add (bin, chain->deinterlace);
1213 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1214 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1217 head = chain->deinterlace;
1219 prev = chain->deinterlace;
1223 pad = gst_element_get_static_pad (head, "sink");
1224 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1225 gst_object_unref (pad);
1227 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1231 pad = gst_element_get_static_pad (prev, "src");
1232 chain->srcpad = gst_ghost_pad_new ("src", pad);
1233 gst_object_unref (pad);
1235 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1238 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1239 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1245 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1246 (NULL), ("Failed to configure the video deinterlace chain."));
1247 free_chain ((GstPlayChain *) chain);
1252 /* make the element (bin) that contains the elements needed to perform
1255 * +------------------------------------------------------------+
1257 * | +-------+ +----------+ +----------+ +---------+ |
1258 * | | queue | |colorspace| |videoscale| |videosink| |
1259 * | +-sink src-sink src-sink src-sink | |
1260 * | | +-------+ +----------+ +----------+ +---------+ |
1262 * +------------------------------------------------------------+
1265 static GstPlayVideoChain *
1266 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1268 GstPlayVideoChain *chain;
1271 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1273 chain = g_new0 (GstPlayVideoChain, 1);
1274 chain->chain.playsink = playsink;
1275 chain->chain.raw = raw;
1277 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1279 if (playsink->video_sink) {
1280 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1281 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1283 /* only try fallback if no specific sink was chosen */
1284 if (chain->sink == NULL) {
1285 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1286 elem = gst_element_factory_make ("autovideosink", "videosink");
1287 chain->sink = try_element (playsink, elem, TRUE);
1289 if (chain->sink == NULL) {
1290 /* if default sink from config.h is different then try it too */
1291 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1292 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1293 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1294 chain->sink = try_element (playsink, elem, TRUE);
1298 playsink->video_sink = gst_object_ref (chain->sink);
1300 if (chain->sink == NULL)
1304 /* if we can disable async behaviour of the sink, we can avoid adding a
1305 * queue for the audio chain. */
1307 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1310 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1311 async, GST_ELEMENT_NAME (elem));
1312 g_object_set (elem, "async", async, NULL);
1313 chain->async = async;
1315 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1316 chain->async = TRUE;
1319 /* find ts-offset element */
1320 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1321 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1324 /* create a bin to hold objects, as we create them we add them to this bin so
1325 * that when something goes wrong we only need to unref the bin */
1326 chain->chain.bin = gst_bin_new ("vbin");
1327 bin = GST_BIN_CAST (chain->chain.bin);
1328 gst_object_ref_sink (bin);
1329 gst_bin_add (bin, chain->sink);
1331 /* decouple decoder from sink, this improves playback quite a lot since the
1332 * decoder can continue while the sink blocks for synchronisation. We don't
1333 * need a lot of buffers as this consumes a lot of memory and we don't want
1334 * too little because else we would be context switching too quickly. */
1335 chain->queue = gst_element_factory_make ("queue", "vqueue");
1336 if (chain->queue == NULL) {
1337 post_missing_element_message (playsink, "queue");
1338 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1339 (_("Missing element '%s' - check your GStreamer installation."),
1340 "queue"), ("video rendering might be suboptimal"));
1344 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1345 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1346 gst_bin_add (bin, chain->queue);
1347 head = prev = chain->queue;
1350 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1351 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1353 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1354 gst_bin_add (bin, chain->conv);
1356 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1357 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1366 GST_DEBUG_OBJECT (playsink, "linking to sink");
1367 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1368 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1372 pad = gst_element_get_static_pad (head, "sink");
1373 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1374 gst_object_unref (pad);
1376 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1383 if (!elem && !playsink->video_sink) {
1384 post_missing_element_message (playsink, "autovideosink");
1385 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1386 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1387 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1388 (_("Both autovideosink and %s elements are missing."),
1389 DEFAULT_VIDEOSINK), (NULL));
1391 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1392 (_("The autovideosink element is missing.")), (NULL));
1395 if (playsink->video_sink) {
1396 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1397 (_("Configured videosink %s is not working."),
1398 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1399 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1400 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1401 (_("Both autovideosink and %s elements are not working."),
1402 DEFAULT_VIDEOSINK), (NULL));
1404 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1405 (_("The autovideosink element is not working.")), (NULL));
1408 free_chain ((GstPlayChain *) chain);
1413 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1414 (NULL), ("Failed to configure the video sink."));
1415 /* checking sink made it READY */
1416 gst_element_set_state (chain->sink, GST_STATE_NULL);
1417 /* Remove chain from the bin to allow reuse later */
1418 gst_bin_remove (bin, chain->sink);
1419 free_chain ((GstPlayChain *) chain);
1425 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1428 GstPlayVideoChain *chain;
1429 GstStateChangeReturn ret;
1431 chain = playsink->videochain;
1433 chain->chain.raw = raw;
1435 /* if the chain was active we don't do anything */
1436 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1439 /* try to set the sink element to READY again */
1440 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1441 if (ret == GST_STATE_CHANGE_FAILURE)
1444 /* find ts-offset element */
1446 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1447 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1450 /* if we can disable async behaviour of the sink, we can avoid adding a
1451 * queue for the audio chain. */
1453 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1456 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1457 async, GST_ELEMENT_NAME (elem));
1458 g_object_set (elem, "async", async, NULL);
1459 chain->async = async;
1461 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1462 chain->async = TRUE;
1467 /* make an element for playback of video with subtitles embedded.
1468 * Only used for *raw* video streams.
1470 * +--------------------------------------------+
1472 * | +--------+ +-----------------+ |
1473 * | | queue | | subtitleoverlay | |
1474 * video--src sink---video_sink | |
1475 * | +--------+ | src--src
1476 * text------------------text_sink | |
1477 * | +-----------------+ |
1478 * +--------------------------------------------+
1481 static GstPlayTextChain *
1482 gen_text_chain (GstPlaySink * playsink)
1484 GstPlayTextChain *chain;
1487 GstPad *videosinkpad, *textsinkpad, *srcpad;
1489 chain = g_new0 (GstPlayTextChain, 1);
1490 chain->chain.playsink = playsink;
1492 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1494 chain->chain.bin = gst_bin_new ("tbin");
1495 bin = GST_BIN_CAST (chain->chain.bin);
1496 gst_object_ref_sink (bin);
1498 videosinkpad = textsinkpad = srcpad = NULL;
1500 /* first try to hook the text pad to the custom sink */
1501 if (playsink->text_sink) {
1502 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1503 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1506 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1509 /* make sure the sparse subtitles don't participate in the preroll */
1510 g_object_set (elem, "async", FALSE, NULL);
1511 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1512 gst_bin_add (bin, chain->sink);
1513 /* NOTE streamsynchronizer needs streams decoupled */
1514 /* make a little queue */
1515 chain->queue = gst_element_factory_make ("queue", "subqueue");
1516 if (chain->queue == NULL) {
1517 post_missing_element_message (playsink, "queue");
1518 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1519 (_("Missing element '%s' - check your GStreamer installation."),
1520 "queue"), ("rendering might be suboptimal"));
1522 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1523 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1524 "silent", TRUE, NULL);
1525 gst_bin_add (bin, chain->queue);
1527 /* we have a custom sink, this will be our textsinkpad */
1528 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1529 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1530 /* we're all fine now and we can add the sink to the chain */
1531 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1532 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1534 GST_WARNING_OBJECT (playsink,
1535 "can't find a sink pad on custom text sink");
1536 gst_bin_remove (bin, chain->sink);
1537 gst_bin_remove (bin, chain->queue);
1539 chain->queue = NULL;
1541 /* try to set sync to true but it's no biggie when we can't */
1542 if (chain->sink && (elem =
1543 gst_play_sink_find_property_sinks (playsink, chain->sink,
1544 "sync", G_TYPE_BOOLEAN)))
1545 g_object_set (elem, "sync", TRUE, NULL);
1548 gst_bin_remove (bin, chain->sink);
1550 GST_WARNING_OBJECT (playsink,
1551 "can't find async property in custom text sink");
1554 if (textsinkpad == NULL) {
1555 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1556 (_("Custom text sink element is not usable.")),
1557 ("fallback to default textoverlay"));
1561 if (textsinkpad == NULL) {
1562 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1563 /* make a little queue */
1564 chain->queue = gst_element_factory_make ("queue", "vqueue");
1565 if (chain->queue == NULL) {
1566 post_missing_element_message (playsink, "queue");
1567 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1568 (_("Missing element '%s' - check your GStreamer installation."),
1569 "queue"), ("video rendering might be suboptimal"));
1571 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1572 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1573 "silent", TRUE, NULL);
1574 gst_bin_add (bin, chain->queue);
1575 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1579 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1580 if (chain->overlay == NULL) {
1581 post_missing_element_message (playsink, "subtitleoverlay");
1582 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1583 (_("Missing element '%s' - check your GStreamer installation."),
1584 "subtitleoverlay"), ("subtitle rendering disabled"));
1586 GstElement *element;
1588 gst_bin_add (bin, chain->overlay);
1590 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1591 if (playsink->font_desc) {
1592 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1593 playsink->font_desc, NULL);
1595 if (playsink->subtitle_encoding) {
1596 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1597 playsink->subtitle_encoding, NULL);
1600 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1601 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1603 /* make another little queue to decouple streams */
1604 element = gst_element_factory_make ("queue", "subqueue");
1605 if (element == NULL) {
1606 post_missing_element_message (playsink, "queue");
1607 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1608 (_("Missing element '%s' - check your GStreamer installation."),
1609 "queue"), ("rendering might be suboptimal"));
1611 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1612 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1613 "silent", TRUE, NULL);
1614 gst_bin_add (bin, element);
1615 if (gst_element_link_pads_full (element, "src", chain->overlay,
1616 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1617 textsinkpad = gst_element_get_static_pad (element, "sink");
1618 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1620 gst_bin_remove (bin, chain->sink);
1621 gst_bin_remove (bin, chain->overlay);
1623 chain->overlay = NULL;
1624 gst_object_unref (videosinkpad);
1625 videosinkpad = NULL;
1632 if (videosinkpad == NULL) {
1633 /* if we still don't have a videosink, we don't have an overlay. the only
1634 * thing we can do is insert an identity and ghost the src
1636 chain->identity = gst_element_factory_make ("identity", "tidentity");
1637 if (chain->identity == NULL) {
1638 post_missing_element_message (playsink, "identity");
1639 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1640 (_("Missing element '%s' - check your GStreamer installation."),
1641 "identity"), (NULL));
1643 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1644 g_object_set (chain->identity, "silent", TRUE, NULL);
1645 gst_bin_add (bin, chain->identity);
1646 srcpad = gst_element_get_static_pad (chain->identity, "src");
1647 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1651 /* expose the ghostpads */
1653 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1654 gst_object_unref (videosinkpad);
1655 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1658 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1659 gst_object_unref (textsinkpad);
1660 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1663 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1664 gst_object_unref (srcpad);
1665 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1672 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1676 g_object_get (object, "volume", &vol, NULL);
1677 playsink->volume = vol;
1679 g_object_notify (G_OBJECT (playsink), "volume");
1683 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1687 g_object_get (object, "mute", &mute, NULL);
1688 playsink->mute = mute;
1690 g_object_notify (G_OBJECT (playsink), "mute");
1693 /* make the chain that contains the elements needed to perform
1696 * We add a tee as the first element so that we can link the visualisation chain
1697 * to it when requested.
1699 * +-------------------------------------------------------------+
1701 * | +---------+ +----------+ +---------+ +---------+ |
1702 * | |audioconv| |audioscale| | volume | |audiosink| |
1703 * | +-srck src-sink src-sink src-sink | |
1704 * | | +---------+ +----------+ +---------+ +---------+ |
1706 * +-------------------------------------------------------------+
1708 static GstPlayAudioChain *
1709 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1711 GstPlayAudioChain *chain;
1713 gboolean have_volume;
1715 GstElement *head, *prev, *elem = NULL;
1717 chain = g_new0 (GstPlayAudioChain, 1);
1718 chain->chain.playsink = playsink;
1719 chain->chain.raw = raw;
1721 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1723 if (playsink->audio_sink) {
1724 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1725 playsink->audio_sink);
1726 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1728 /* only try fallback if no specific sink was chosen */
1729 if (chain->sink == NULL) {
1730 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1731 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1732 chain->sink = try_element (playsink, elem, TRUE);
1734 if (chain->sink == NULL) {
1735 /* if default sink from config.h is different then try it too */
1736 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1737 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1738 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1739 chain->sink = try_element (playsink, elem, TRUE);
1743 playsink->audio_sink = gst_object_ref (chain->sink);
1745 if (chain->sink == NULL)
1748 chain->chain.bin = gst_bin_new ("abin");
1749 bin = GST_BIN_CAST (chain->chain.bin);
1750 gst_object_ref_sink (bin);
1751 gst_bin_add (bin, chain->sink);
1753 /* we have to add a queue when we need to decouple for the video sink in
1754 * visualisations and for streamsynchronizer */
1755 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1756 chain->queue = gst_element_factory_make ("queue", "aqueue");
1757 if (chain->queue == NULL) {
1758 post_missing_element_message (playsink, "queue");
1759 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1760 (_("Missing element '%s' - check your GStreamer installation."),
1761 "queue"), ("audio playback and visualizations might not work"));
1765 g_object_set (chain->queue, "silent", TRUE, NULL);
1766 gst_bin_add (bin, chain->queue);
1767 prev = head = chain->queue;
1770 /* find ts-offset element */
1771 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1772 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1775 /* check if the sink, or something within the sink, has the volume property.
1776 * If it does we don't need to add a volume element. */
1778 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1781 chain->volume = elem;
1783 g_signal_connect (chain->volume, "notify::volume",
1784 G_CALLBACK (notify_volume_cb), playsink);
1786 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1788 chain->sink_volume = TRUE;
1789 /* if the sink also has a mute property we can use this as well. We'll only
1790 * use the mute property if there is a volume property. We can simulate the
1791 * mute with the volume otherwise. */
1793 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1796 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1797 g_signal_connect (chain->mute, "notify::mute",
1798 G_CALLBACK (notify_mute_cb), playsink);
1800 /* use the sink to control the volume and mute */
1801 if (playsink->volume_changed) {
1802 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1803 playsink->volume_changed = FALSE;
1805 if (playsink->mute_changed) {
1807 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1810 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1812 playsink->mute_changed = FALSE;
1815 /* no volume, we need to add a volume element when we can */
1816 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1817 have_volume = FALSE;
1818 chain->sink_volume = FALSE;
1821 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1822 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1823 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1824 gboolean use_volume =
1825 !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME;
1826 GST_DEBUG_OBJECT (playsink,
1827 "creating audioconvert with use-converters %d, use-volume %d",
1828 use_converters, use_volume);
1830 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1831 "use-converters", use_converters, "use-volume", use_volume, NULL);
1832 gst_bin_add (bin, chain->conv);
1834 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1835 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1842 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1843 GstPlaySinkAudioConvert *conv =
1844 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1847 chain->volume = conv->volume;
1850 g_signal_connect (chain->volume, "notify::volume",
1851 G_CALLBACK (notify_volume_cb), playsink);
1853 /* volume also has the mute property */
1854 chain->mute = chain->volume;
1855 g_signal_connect (chain->mute, "notify::mute",
1856 G_CALLBACK (notify_mute_cb), playsink);
1858 /* configure with the latest volume and mute */
1859 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1861 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1867 /* we only have to link to the previous element if we have something in
1868 * front of the sink */
1869 GST_DEBUG_OBJECT (playsink, "linking to sink");
1870 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1871 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1875 /* post a warning if we have no way to configure the volume */
1877 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1878 (_("No volume control found")), ("Volume/mute is not available"));
1881 /* and ghost the sinkpad of the headmost element */
1882 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1883 pad = gst_element_get_static_pad (head, "sink");
1884 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1885 gst_object_unref (pad);
1886 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1893 if (!elem && !playsink->audio_sink) {
1894 post_missing_element_message (playsink, "autoaudiosink");
1895 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1896 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1897 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1898 (_("Both autoaudiosink and %s elements are missing."),
1899 DEFAULT_AUDIOSINK), (NULL));
1901 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1902 (_("The autoaudiosink element is missing.")), (NULL));
1905 if (playsink->audio_sink) {
1906 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1907 (_("Configured audiosink %s is not working."),
1908 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1909 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1910 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1911 (_("Both autoaudiosink and %s elements are not working."),
1912 DEFAULT_AUDIOSINK), (NULL));
1914 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1915 (_("The autoaudiosink element is not working.")), (NULL));
1918 free_chain ((GstPlayChain *) chain);
1923 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1924 (NULL), ("Failed to configure the audio sink."));
1925 /* checking sink made it READY */
1926 gst_element_set_state (chain->sink, GST_STATE_NULL);
1927 /* Remove chain from the bin to allow reuse later */
1928 gst_bin_remove (bin, chain->sink);
1929 free_chain ((GstPlayChain *) chain);
1935 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1938 GstPlayAudioChain *chain;
1939 GstStateChangeReturn ret;
1941 chain = playsink->audiochain;
1943 chain->chain.raw = raw;
1945 /* if the chain was active we don't do anything */
1946 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1949 /* try to set the sink element to READY again */
1950 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1951 if (ret == GST_STATE_CHANGE_FAILURE)
1954 /* find ts-offset element */
1955 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1956 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1959 /* check if the sink, or something within the sink, has the volume property.
1960 * If it does we don't need to add a volume element. */
1962 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1965 chain->volume = elem;
1967 if (playsink->volume_changed) {
1968 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1970 /* use the sink to control the volume */
1971 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1972 playsink->volume_changed = FALSE;
1975 g_signal_connect (chain->volume, "notify::volume",
1976 G_CALLBACK (notify_volume_cb), playsink);
1977 /* if the sink also has a mute property we can use this as well. We'll only
1978 * use the mute property if there is a volume property. We can simulate the
1979 * mute with the volume otherwise. */
1981 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1984 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1985 g_signal_connect (chain->mute, "notify::mute",
1986 G_CALLBACK (notify_mute_cb), playsink);
1989 g_object_set (chain->conv, "use-volume", FALSE, NULL);
1991 GstPlaySinkAudioConvert *conv =
1992 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1994 /* no volume, we need to add a volume element when we can */
1995 g_object_set (chain->conv, "use-volume", TRUE, NULL);
1996 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1998 /* Disconnect signals */
1999 disconnect_chain (chain, playsink);
2002 chain->volume = conv->volume;
2003 chain->mute = chain->volume;
2005 g_signal_connect (chain->volume, "notify::volume",
2006 G_CALLBACK (notify_volume_cb), playsink);
2008 g_signal_connect (chain->mute, "notify::mute",
2009 G_CALLBACK (notify_mute_cb), playsink);
2011 /* configure with the latest volume and mute */
2012 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2013 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2016 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2022 * +-------------------------------------------------------------------+
2024 * | +----------+ +------------+ +----------+ +-------+ |
2025 * | | visqueue | | audioconv | | audiores | | vis | |
2026 * | +-sink src-sink + samp src-sink src-sink src-+ |
2027 * | | +----------+ +------------+ +----------+ +-------+ | |
2029 * +-------------------------------------------------------------------+
2032 static GstPlayVisChain *
2033 gen_vis_chain (GstPlaySink * playsink)
2035 GstPlayVisChain *chain;
2041 chain = g_new0 (GstPlayVisChain, 1);
2042 chain->chain.playsink = playsink;
2044 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2046 chain->chain.bin = gst_bin_new ("visbin");
2047 bin = GST_BIN_CAST (chain->chain.bin);
2048 gst_object_ref_sink (bin);
2050 /* we're queuing raw audio here, we can remove this queue when we can disable
2051 * async behaviour in the video sink. */
2052 chain->queue = gst_element_factory_make ("queue", "visqueue");
2053 if (chain->queue == NULL)
2055 g_object_set (chain->queue, "silent", TRUE, NULL);
2056 gst_bin_add (bin, chain->queue);
2058 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2059 if (chain->conv == NULL)
2060 goto no_audioconvert;
2061 gst_bin_add (bin, chain->conv);
2063 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2064 if (chain->resample == NULL)
2065 goto no_audioresample;
2066 gst_bin_add (bin, chain->resample);
2068 /* this pad will be used for blocking the dataflow and switching the vis
2070 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2072 if (playsink->visualisation) {
2073 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2074 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2076 if (chain->vis == NULL) {
2077 GST_DEBUG_OBJECT (playsink, "trying goom");
2078 elem = gst_element_factory_make ("goom", "vis");
2079 chain->vis = try_element (playsink, elem, TRUE);
2081 if (chain->vis == NULL)
2084 gst_bin_add (bin, chain->vis);
2086 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2087 GST_PAD_LINK_CHECK_NOTHING);
2089 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2090 GST_PAD_LINK_CHECK_NOTHING);
2092 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2093 GST_PAD_LINK_CHECK_NOTHING);
2097 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2098 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2100 pad = gst_element_get_static_pad (chain->queue, "sink");
2101 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2102 gst_object_unref (pad);
2103 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2105 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2106 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2113 post_missing_element_message (playsink, "queue");
2114 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2115 (_("Missing element '%s' - check your GStreamer installation."),
2117 free_chain ((GstPlayChain *) chain);
2122 post_missing_element_message (playsink, "audioconvert");
2123 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2124 (_("Missing element '%s' - check your GStreamer installation."),
2125 "audioconvert"), ("possibly a liboil version mismatch?"));
2126 free_chain ((GstPlayChain *) chain);
2131 post_missing_element_message (playsink, "audioresample");
2132 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2133 (_("Missing element '%s' - check your GStreamer installation."),
2134 "audioresample"), (NULL));
2135 free_chain ((GstPlayChain *) chain);
2140 post_missing_element_message (playsink, "goom");
2141 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2142 (_("Missing element '%s' - check your GStreamer installation."),
2144 free_chain ((GstPlayChain *) chain);
2149 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2150 (NULL), ("Failed to configure the visualisation element."));
2151 /* element made it to READY */
2152 gst_element_set_state (chain->vis, GST_STATE_NULL);
2153 free_chain ((GstPlayChain *) chain);
2158 /* this function is called when all the request pads are requested and when we
2159 * have to construct the final pipeline. Based on the flags we construct the
2160 * final output pipelines.
2163 gst_play_sink_reconfigure (GstPlaySink * playsink)
2166 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2168 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2170 /* assume we need nothing */
2171 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2173 GST_PLAY_SINK_LOCK (playsink);
2174 GST_OBJECT_LOCK (playsink);
2175 /* get flags, there are protected with the object lock */
2176 flags = playsink->flags;
2177 GST_OBJECT_UNLOCK (playsink);
2179 /* figure out which components we need */
2180 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2181 /* we have subtitles and we are requested to show it */
2185 if (((flags & GST_PLAY_FLAG_VIDEO)
2186 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2187 /* we have video and we are requested to show it */
2190 /* we only deinterlace if native video is not requested and
2191 * we have raw video */
2192 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2193 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2194 need_deinterlace = TRUE;
2197 if (playsink->audio_pad) {
2198 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2201 if (playsink->audio_pad_raw) {
2202 /* only can do vis with raw uncompressed audio */
2203 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2204 /* also add video when we add visualisation */
2211 /* we have a text_pad and we need text rendering, in this case we need a
2212 * video_pad to combine the video with the text or visualizations */
2213 if (need_text && !need_video) {
2214 if (playsink->video_pad) {
2216 } else if (need_audio) {
2217 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2218 (_("Can't play a text file without video or visualizations.")),
2219 ("Have text pad but no video pad or visualizations"));
2222 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2223 (_("Can't play a text file without video or visualizations.")),
2224 ("Have text pad but no video pad or visualizations"));
2225 GST_PLAY_SINK_UNLOCK (playsink);
2230 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2231 need_video, need_vis, need_text);
2233 /* set up video pipeline */
2235 gboolean raw, async;
2237 /* we need a raw sink when we do vis or when we have a raw pad */
2238 raw = need_vis ? TRUE : playsink->video_pad_raw;
2239 /* we try to set the sink async=FALSE when we need vis, this way we can
2240 * avoid a queue in the audio chain. */
2243 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2244 playsink->video_pad_raw);
2246 if (playsink->videochain) {
2247 /* try to reactivate the chain */
2248 if (!setup_video_chain (playsink, raw, async)) {
2249 if (playsink->video_sinkpad_stream_synchronizer) {
2250 gst_element_release_request_pad (GST_ELEMENT_CAST
2251 (playsink->stream_synchronizer),
2252 playsink->video_sinkpad_stream_synchronizer);
2253 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2254 playsink->video_sinkpad_stream_synchronizer = NULL;
2255 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2256 playsink->video_srcpad_stream_synchronizer = NULL;
2259 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2261 /* Remove the sink from the bin to keep its state
2262 * and unparent it to allow reuse */
2263 if (playsink->videochain->sink)
2264 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2265 playsink->videochain->sink);
2267 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2268 free_chain ((GstPlayChain *) playsink->videochain);
2269 playsink->videochain = NULL;
2273 if (!playsink->videochain)
2274 playsink->videochain = gen_video_chain (playsink, raw, async);
2275 if (!playsink->videochain)
2278 if (!playsink->video_sinkpad_stream_synchronizer) {
2281 playsink->video_sinkpad_stream_synchronizer =
2282 gst_element_get_request_pad (GST_ELEMENT_CAST
2283 (playsink->stream_synchronizer), "sink_%d");
2284 it = gst_pad_iterate_internal_links
2285 (playsink->video_sinkpad_stream_synchronizer);
2287 gst_iterator_next (it,
2288 (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2289 g_assert (playsink->video_srcpad_stream_synchronizer);
2290 gst_iterator_free (it);
2293 if (playsink->video_pad)
2294 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2295 playsink->video_sinkpad_stream_synchronizer);
2297 if (need_deinterlace) {
2298 if (!playsink->videodeinterlacechain)
2299 playsink->videodeinterlacechain =
2300 gen_video_deinterlace_chain (playsink);
2301 if (!playsink->videodeinterlacechain)
2304 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2306 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2308 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2309 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2311 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2312 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2314 if (playsink->videodeinterlacechain) {
2315 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2316 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2321 GST_DEBUG_OBJECT (playsink, "adding video chain");
2322 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2323 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2324 /* if we are not part of vis or subtitles, set the ghostpad target */
2325 if (!need_vis && !need_text && (!playsink->textchain
2326 || !playsink->text_pad)) {
2327 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2328 if (need_deinterlace)
2329 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2330 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2332 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2333 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2336 GST_DEBUG_OBJECT (playsink, "no video needed");
2337 if (playsink->videochain) {
2338 GST_DEBUG_OBJECT (playsink, "removing video chain");
2339 if (playsink->vischain) {
2342 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2344 /* also had visualisation, release the tee srcpad before we then
2345 * unlink the video from it */
2346 if (playsink->audio_tee_vissrc) {
2347 gst_element_release_request_pad (playsink->audio_tee,
2348 playsink->audio_tee_vissrc);
2349 gst_object_unref (playsink->audio_tee_vissrc);
2350 playsink->audio_tee_vissrc = NULL;
2353 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2354 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2357 if (playsink->video_sinkpad_stream_synchronizer) {
2358 gst_element_release_request_pad (GST_ELEMENT_CAST
2359 (playsink->stream_synchronizer),
2360 playsink->video_sinkpad_stream_synchronizer);
2361 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2362 playsink->video_sinkpad_stream_synchronizer = NULL;
2363 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2364 playsink->video_srcpad_stream_synchronizer = NULL;
2367 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2368 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2369 if (playsink->videochain->ts_offset)
2370 gst_object_unref (playsink->videochain->ts_offset);
2371 playsink->videochain->ts_offset = NULL;
2374 if (playsink->videodeinterlacechain) {
2375 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2376 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2379 if (playsink->video_pad)
2380 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2386 GST_DEBUG_OBJECT (playsink, "adding audio");
2388 /* get a raw sink if we are asked for a raw pad */
2389 raw = playsink->audio_pad_raw;
2391 if (playsink->audiochain) {
2392 /* try to reactivate the chain */
2393 if (!setup_audio_chain (playsink, raw)) {
2394 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2395 if (playsink->audio_tee_asrc) {
2396 gst_element_release_request_pad (playsink->audio_tee,
2397 playsink->audio_tee_asrc);
2398 gst_object_unref (playsink->audio_tee_asrc);
2399 playsink->audio_tee_asrc = NULL;
2402 if (playsink->audio_sinkpad_stream_synchronizer) {
2403 gst_element_release_request_pad (GST_ELEMENT_CAST
2404 (playsink->stream_synchronizer),
2405 playsink->audio_sinkpad_stream_synchronizer);
2406 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2407 playsink->audio_sinkpad_stream_synchronizer = NULL;
2408 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2409 playsink->audio_srcpad_stream_synchronizer = NULL;
2412 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2414 /* Remove the sink from the bin to keep its state
2415 * and unparent it to allow reuse */
2416 if (playsink->audiochain->sink)
2417 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2418 playsink->audiochain->sink);
2420 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2421 disconnect_chain (playsink->audiochain, playsink);
2422 playsink->audiochain->volume = NULL;
2423 playsink->audiochain->mute = NULL;
2424 if (playsink->audiochain->ts_offset)
2425 gst_object_unref (playsink->audiochain->ts_offset);
2426 playsink->audiochain->ts_offset = NULL;
2427 free_chain ((GstPlayChain *) playsink->audiochain);
2428 playsink->audiochain = NULL;
2429 playsink->volume_changed = playsink->mute_changed = FALSE;
2433 if (!playsink->audiochain) {
2434 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2435 playsink->audiochain = gen_audio_chain (playsink, raw);
2438 if (!playsink->audio_sinkpad_stream_synchronizer) {
2441 playsink->audio_sinkpad_stream_synchronizer =
2442 gst_element_get_request_pad (GST_ELEMENT_CAST
2443 (playsink->stream_synchronizer), "sink_%d");
2444 it = gst_pad_iterate_internal_links
2445 (playsink->audio_sinkpad_stream_synchronizer);
2447 gst_iterator_next (it,
2448 (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2449 g_assert (playsink->audio_srcpad_stream_synchronizer);
2450 gst_iterator_free (it);
2453 if (playsink->audiochain) {
2454 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2455 if (playsink->audio_tee_asrc == NULL) {
2456 playsink->audio_tee_asrc =
2457 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2459 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2460 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2461 gst_pad_link_full (playsink->audio_tee_asrc,
2462 playsink->audio_sinkpad_stream_synchronizer,
2463 GST_PAD_LINK_CHECK_NOTHING);
2464 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2465 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2468 GST_DEBUG_OBJECT (playsink, "no audio needed");
2469 /* we have no audio or we are requested to not play audio */
2470 if (playsink->audiochain) {
2471 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2472 /* release the audio pad */
2473 if (playsink->audio_tee_asrc) {
2474 gst_element_release_request_pad (playsink->audio_tee,
2475 playsink->audio_tee_asrc);
2476 gst_object_unref (playsink->audio_tee_asrc);
2477 playsink->audio_tee_asrc = NULL;
2480 if (playsink->audio_sinkpad_stream_synchronizer) {
2481 gst_element_release_request_pad (GST_ELEMENT_CAST
2482 (playsink->stream_synchronizer),
2483 playsink->audio_sinkpad_stream_synchronizer);
2484 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2485 playsink->audio_sinkpad_stream_synchronizer = NULL;
2486 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2487 playsink->audio_srcpad_stream_synchronizer = NULL;
2490 if (playsink->audiochain->sink_volume) {
2491 disconnect_chain (playsink->audiochain, playsink);
2492 playsink->audiochain->volume = NULL;
2493 playsink->audiochain->mute = NULL;
2494 if (playsink->audiochain->ts_offset)
2495 gst_object_unref (playsink->audiochain->ts_offset);
2496 playsink->audiochain->ts_offset = NULL;
2498 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2499 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2506 if (!playsink->vischain)
2507 playsink->vischain = gen_vis_chain (playsink);
2509 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2511 if (playsink->vischain) {
2512 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2514 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2515 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2516 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2517 if (playsink->audio_tee_vissrc == NULL) {
2518 playsink->audio_tee_vissrc =
2519 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2521 gst_pad_link_full (playsink->audio_tee_vissrc,
2522 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2523 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2524 GST_PAD_LINK_CHECK_NOTHING);
2525 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2526 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2527 gst_object_unref (srcpad);
2530 GST_DEBUG_OBJECT (playsink, "no vis needed");
2531 if (playsink->vischain) {
2532 if (playsink->audio_tee_vissrc) {
2533 gst_element_release_request_pad (playsink->audio_tee,
2534 playsink->audio_tee_vissrc);
2535 gst_object_unref (playsink->audio_tee_vissrc);
2536 playsink->audio_tee_vissrc = NULL;
2538 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2539 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2540 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2545 GST_DEBUG_OBJECT (playsink, "adding text");
2546 if (!playsink->textchain) {
2547 GST_DEBUG_OBJECT (playsink, "creating text chain");
2548 playsink->textchain = gen_text_chain (playsink);
2550 if (playsink->textchain) {
2553 GST_DEBUG_OBJECT (playsink, "adding text chain");
2554 if (playsink->textchain->overlay)
2555 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2557 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2559 if (!playsink->text_sinkpad_stream_synchronizer) {
2560 playsink->text_sinkpad_stream_synchronizer =
2561 gst_element_get_request_pad (GST_ELEMENT_CAST
2562 (playsink->stream_synchronizer), "sink_%d");
2563 it = gst_pad_iterate_internal_links
2564 (playsink->text_sinkpad_stream_synchronizer);
2566 gst_iterator_next (it,
2567 (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2568 g_assert (playsink->text_srcpad_stream_synchronizer);
2569 gst_iterator_free (it);
2571 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2572 playsink->text_sinkpad_stream_synchronizer);
2573 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2574 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2581 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2582 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2583 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2584 GST_PAD_LINK_CHECK_NOTHING);
2585 gst_object_unref (srcpad);
2587 if (need_deinterlace)
2588 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2589 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2591 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2592 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2594 gst_pad_link_full (playsink->textchain->srcpad,
2595 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2597 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2600 GST_DEBUG_OBJECT (playsink, "no text needed");
2601 /* we have no subtitles/text or we are requested to not show them */
2603 if (playsink->text_sinkpad_stream_synchronizer) {
2604 gst_element_release_request_pad (GST_ELEMENT_CAST
2605 (playsink->stream_synchronizer),
2606 playsink->text_sinkpad_stream_synchronizer);
2607 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2608 playsink->text_sinkpad_stream_synchronizer = NULL;
2609 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2610 playsink->text_srcpad_stream_synchronizer = NULL;
2613 if (playsink->textchain) {
2614 if (playsink->text_pad == NULL) {
2615 /* no text pad, remove the chain entirely */
2616 GST_DEBUG_OBJECT (playsink, "removing text chain");
2617 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2618 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2620 /* we have a chain and a textpad, turn the subtitles off */
2621 GST_DEBUG_OBJECT (playsink, "turning off the text");
2622 if (playsink->textchain->overlay)
2623 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2627 if (!need_video && playsink->video_pad) {
2628 if (playsink->video_sinkpad_stream_synchronizer) {
2629 gst_element_release_request_pad (GST_ELEMENT_CAST
2630 (playsink->stream_synchronizer),
2631 playsink->video_sinkpad_stream_synchronizer);
2632 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2633 playsink->video_sinkpad_stream_synchronizer = NULL;
2634 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2635 playsink->video_srcpad_stream_synchronizer = NULL;
2638 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2641 if (playsink->text_pad && !playsink->textchain)
2642 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2644 update_av_offset (playsink);
2645 do_async_done (playsink);
2646 GST_PLAY_SINK_UNLOCK (playsink);
2653 /* gen_ chain already posted error */
2654 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2655 GST_PLAY_SINK_UNLOCK (playsink);
2661 * gst_play_sink_set_flags:
2662 * @playsink: a #GstPlaySink
2663 * @flags: #GstPlayFlags
2665 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2666 * when constructing the sink pipelins.
2668 * Returns: TRUE if the flags could be configured.
2671 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2673 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2675 GST_OBJECT_LOCK (playsink);
2676 playsink->flags = flags;
2677 GST_OBJECT_UNLOCK (playsink);
2683 * gst_play_sink_get_flags:
2684 * @playsink: a #GstPlaySink
2686 * Get the flags of @playsink. That flags control the behaviour of the sink when
2687 * it constructs the sink pipelines.
2689 * Returns: the currently configured #GstPlayFlags.
2692 gst_play_sink_get_flags (GstPlaySink * playsink)
2696 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2698 GST_OBJECT_LOCK (playsink);
2699 res = playsink->flags;
2700 GST_OBJECT_UNLOCK (playsink);
2706 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2708 GstPlayTextChain *chain;
2710 GST_PLAY_SINK_LOCK (playsink);
2711 chain = (GstPlayTextChain *) playsink->textchain;
2712 g_free (playsink->font_desc);
2713 playsink->font_desc = g_strdup (desc);
2714 if (chain && chain->overlay) {
2715 g_object_set (chain->overlay, "font-desc", desc, NULL);
2717 GST_PLAY_SINK_UNLOCK (playsink);
2721 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2723 gchar *result = NULL;
2724 GstPlayTextChain *chain;
2726 GST_PLAY_SINK_LOCK (playsink);
2727 chain = (GstPlayTextChain *) playsink->textchain;
2728 if (chain && chain->overlay) {
2729 g_object_get (chain->overlay, "font-desc", &result, NULL);
2730 playsink->font_desc = g_strdup (result);
2732 result = g_strdup (playsink->font_desc);
2734 GST_PLAY_SINK_UNLOCK (playsink);
2740 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2741 const gchar * encoding)
2743 GstPlayTextChain *chain;
2745 GST_PLAY_SINK_LOCK (playsink);
2746 chain = (GstPlayTextChain *) playsink->textchain;
2747 g_free (playsink->subtitle_encoding);
2748 playsink->subtitle_encoding = g_strdup (encoding);
2749 if (chain && chain->overlay) {
2750 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2752 GST_PLAY_SINK_UNLOCK (playsink);
2756 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2758 gchar *result = NULL;
2759 GstPlayTextChain *chain;
2761 GST_PLAY_SINK_LOCK (playsink);
2762 chain = (GstPlayTextChain *) playsink->textchain;
2763 if (chain && chain->overlay) {
2764 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2765 playsink->subtitle_encoding = g_strdup (result);
2767 result = g_strdup (playsink->subtitle_encoding);
2769 GST_PLAY_SINK_UNLOCK (playsink);
2775 update_av_offset (GstPlaySink * playsink)
2778 GstPlayAudioChain *achain;
2779 GstPlayVideoChain *vchain;
2781 av_offset = playsink->av_offset;
2782 achain = (GstPlayAudioChain *) playsink->audiochain;
2783 vchain = (GstPlayVideoChain *) playsink->videochain;
2785 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2786 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2787 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2789 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2794 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2796 GST_PLAY_SINK_LOCK (playsink);
2797 playsink->av_offset = av_offset;
2798 update_av_offset (playsink);
2799 GST_PLAY_SINK_UNLOCK (playsink);
2803 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2807 GST_PLAY_SINK_LOCK (playsink);
2808 result = playsink->av_offset;
2809 GST_PLAY_SINK_UNLOCK (playsink);
2815 * gst_play_sink_get_last_frame:
2816 * @playsink: a #GstPlaySink
2818 * Get the last displayed frame from @playsink. This frame is in the native
2819 * format of the sink element, the caps on the result buffer contain the format
2820 * of the frame data.
2822 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2826 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2828 GstBuffer *result = NULL;
2829 GstPlayVideoChain *chain;
2831 GST_PLAY_SINK_LOCK (playsink);
2832 GST_DEBUG_OBJECT (playsink, "taking last frame");
2833 /* get the video chain if we can */
2834 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2835 GST_DEBUG_OBJECT (playsink, "found video chain");
2836 /* see if the chain is active */
2837 if (chain->chain.activated && chain->sink) {
2840 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2842 /* find and get the last-buffer property now */
2844 gst_play_sink_find_property (playsink, chain->sink,
2845 "last-buffer", GST_TYPE_BUFFER))) {
2846 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2847 g_object_get (elem, "last-buffer", &result, NULL);
2848 gst_object_unref (elem);
2852 GST_PLAY_SINK_UNLOCK (playsink);
2858 * gst_play_sink_convert_frame:
2859 * @playsink: a #GstPlaySink
2862 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2863 * be in the native format of the sink element and the caps on the buffer
2864 * describe the format of the frame. If @caps is not %NULL, the video
2865 * frame will be converted to the format of the caps.
2867 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2868 * available or when the conversion failed.
2871 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2875 result = gst_play_sink_get_last_frame (playsink);
2876 if (result != NULL && caps != NULL) {
2880 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2881 gst_buffer_unref (result);
2882 if (temp == NULL && err) {
2883 /* I'm really uncertain whether we should make playsink post an error
2884 * on the bus or not. It's not like it's a critical issue regarding
2885 * playsink behaviour. */
2886 GST_ERROR ("Error converting frame: %s", err->message);
2895 is_raw_structure (GstStructure * s)
2899 name = gst_structure_get_name (s);
2901 if (g_str_has_prefix (name, "video/x-raw-") ||
2902 g_str_has_prefix (name, "audio/x-raw-"))
2908 is_raw_pad (GstPad * pad)
2910 GstPad *peer = gst_pad_get_peer (pad);
2912 gboolean raw = TRUE;
2917 caps = gst_pad_get_negotiated_caps (peer);
2921 caps = gst_pad_get_caps_reffed (peer);
2923 n = gst_caps_get_size (caps);
2924 for (i = 0; i < n; i++) {
2925 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2929 } else if (raw != r) {
2930 GST_ERROR_OBJECT (pad,
2931 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2937 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2939 gst_caps_unref (caps);
2940 gst_object_unref (peer);
2946 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
2948 GstPlaySink *playsink = (GstPlaySink *) user_data;
2951 GST_PLAY_SINK_LOCK (playsink);
2953 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2954 if (pad == playsink->video_pad) {
2955 playsink->video_pad_blocked = blocked;
2956 GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
2958 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2959 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2961 } else if (pad == playsink->audio_pad) {
2962 playsink->audio_pad_blocked = blocked;
2963 GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
2965 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2966 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2968 } else if (pad == playsink->text_pad) {
2969 playsink->text_pad_blocked = blocked;
2970 GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
2972 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
2976 gst_object_unref (pad);
2977 GST_PLAY_SINK_UNLOCK (playsink);
2981 /* We reconfigure when for ALL streams:
2982 * * there isn't a pad
2983 * * OR the pad is blocked
2984 * * OR there are no pending blocks on that pad
2987 if ((!playsink->video_pad || playsink->video_pad_blocked
2988 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
2989 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
2990 && (!playsink->text_pad || playsink->text_pad_blocked
2991 || !PENDING_TEXT_BLOCK (playsink))) {
2992 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
2994 if (playsink->video_pad) {
2995 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
2996 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
2997 playsink->video_pad_raw);
3000 if (playsink->audio_pad) {
3001 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3002 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3003 playsink->audio_pad_raw);
3006 gst_play_sink_reconfigure (playsink);
3008 if (playsink->video_pad) {
3010 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3011 (playsink->video_pad)));
3012 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3013 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3014 gst_object_unref (opad);
3017 if (playsink->audio_pad) {
3019 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3020 (playsink->audio_pad)));
3021 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3022 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3023 gst_object_unref (opad);
3026 if (playsink->text_pad) {
3028 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3029 (playsink->text_pad)));
3030 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3031 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3032 gst_object_unref (opad);
3036 gst_object_unref (pad);
3038 GST_PLAY_SINK_UNLOCK (playsink);
3042 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3044 gboolean reconfigure = FALSE;
3048 g_object_get (pad, "caps", &caps, NULL);
3052 if (pad == playsink->audio_pad) {
3053 raw = is_raw_pad (pad);
3054 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3055 && playsink->audiochain;
3056 GST_DEBUG_OBJECT (pad,
3057 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3059 } else if (pad == playsink->video_pad) {
3060 raw = is_raw_pad (pad);
3061 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3062 && playsink->videochain;
3063 GST_DEBUG_OBJECT (pad,
3064 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3068 gst_caps_unref (caps);
3071 GST_PLAY_SINK_LOCK (playsink);
3072 if (playsink->video_pad) {
3074 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3075 (playsink->video_pad)));
3076 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3077 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3078 gst_object_unref (opad);
3081 if (playsink->audio_pad) {
3083 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3084 (playsink->audio_pad)));
3085 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3086 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3087 gst_object_unref (opad);
3090 if (playsink->text_pad) {
3092 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3093 (playsink->text_pad)));
3094 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3095 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3096 gst_object_unref (opad);
3098 GST_PLAY_SINK_UNLOCK (playsink);
3103 * gst_play_sink_request_pad
3104 * @playsink: a #GstPlaySink
3105 * @type: a #GstPlaySinkType
3107 * Create or return a pad of @type.
3109 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3112 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3115 gboolean created = FALSE;
3116 gboolean activate = TRUE;
3117 const gchar *pad_name = NULL;
3119 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3121 GST_PLAY_SINK_LOCK (playsink);
3123 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3124 case GST_PLAY_SINK_TYPE_AUDIO:
3125 pad_name = "audio_sink";
3126 if (!playsink->audio_tee) {
3127 GST_LOG_OBJECT (playsink, "creating tee");
3128 /* create tee when needed. This element will feed the audio sink chain
3129 * and the vis chain. */
3130 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3131 if (playsink->audio_tee == NULL) {
3132 post_missing_element_message (playsink, "tee");
3133 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3134 (_("Missing element '%s' - check your GStreamer installation."),
3139 playsink->audio_tee_sink =
3140 gst_element_get_static_pad (playsink->audio_tee, "sink");
3141 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3142 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3145 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3147 if (!playsink->audio_pad) {
3148 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3149 playsink->audio_pad =
3150 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3151 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3152 G_CALLBACK (caps_notify_cb), playsink);
3155 playsink->audio_pad_raw = FALSE;
3156 res = playsink->audio_pad;
3158 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3159 case GST_PLAY_SINK_TYPE_VIDEO:
3160 pad_name = "video_sink";
3161 if (!playsink->video_pad) {
3162 GST_LOG_OBJECT (playsink, "ghosting videosink");
3163 playsink->video_pad =
3164 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3165 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3166 G_CALLBACK (caps_notify_cb), playsink);
3169 playsink->video_pad_raw = FALSE;
3170 res = playsink->video_pad;
3172 case GST_PLAY_SINK_TYPE_TEXT:
3173 GST_LOG_OBJECT (playsink, "ghosting text");
3174 if (!playsink->text_pad) {
3175 playsink->text_pad =
3176 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3179 res = playsink->text_pad;
3181 case GST_PLAY_SINK_TYPE_FLUSHING:
3185 /* we need a unique padname for the flushing pad. */
3186 padname = g_strdup_printf ("flushing_%d", playsink->count);
3187 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3198 GST_PLAY_SINK_UNLOCK (playsink);
3200 if (created && res) {
3201 /* we have to add the pad when it's active or we get an error when the
3202 * element is 'running' */
3203 gst_pad_set_active (res, TRUE);
3204 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3205 if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3207 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3209 gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3210 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3211 PENDING_FLAG_SET (playsink, type);
3212 gst_object_unref (blockpad);
3215 gst_pad_set_active (res, activate);
3222 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3227 GstPlaySinkType type;
3228 const gchar *tplname;
3230 g_return_val_if_fail (templ != NULL, NULL);
3232 GST_DEBUG_OBJECT (element, "name:%s", name);
3234 psink = GST_PLAY_SINK (element);
3235 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3237 /* Figure out the GstPlaySinkType based on the template */
3238 if (!strcmp (tplname, "audio_sink"))
3239 type = GST_PLAY_SINK_TYPE_AUDIO;
3240 else if (!strcmp (tplname, "audio_raw_sink"))
3241 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3242 else if (!strcmp (tplname, "video_sink"))
3243 type = GST_PLAY_SINK_TYPE_VIDEO;
3244 else if (!strcmp (tplname, "video_raw_sink"))
3245 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3246 else if (!strcmp (tplname, "text_sink"))
3247 type = GST_PLAY_SINK_TYPE_TEXT;
3249 goto unknown_template;
3251 pad = gst_play_sink_request_pad (psink, type);
3255 GST_WARNING_OBJECT (element, "Unknown pad template");
3260 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3262 GstPad **res = NULL;
3263 gboolean untarget = TRUE;
3265 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3267 GST_PLAY_SINK_LOCK (playsink);
3268 if (pad == playsink->video_pad) {
3269 res = &playsink->video_pad;
3270 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3272 } else if (pad == playsink->audio_pad) {
3273 res = &playsink->audio_pad;
3274 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3276 } else if (pad == playsink->text_pad) {
3277 res = &playsink->text_pad;
3279 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3283 GST_PLAY_SINK_UNLOCK (playsink);
3286 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3287 gst_pad_set_active (*res, FALSE);
3289 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3290 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3292 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3293 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3299 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3301 GstPlaySink *psink = GST_PLAY_SINK (element);
3303 gst_play_sink_release_pad (psink, pad);
3307 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3309 GstPlaySink *playsink;
3311 playsink = GST_PLAY_SINK_CAST (bin);
3313 switch (GST_MESSAGE_TYPE (message)) {
3314 case GST_MESSAGE_STEP_DONE:
3319 gboolean flush, intermediate, eos;
3322 GST_INFO_OBJECT (playsink, "Handling step-done message");
3323 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3324 &intermediate, &duration, &eos);
3326 if (format == GST_FORMAT_BUFFERS) {
3327 /* for the buffer format, we align the other streams */
3328 if (playsink->audiochain) {
3332 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3335 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3336 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3340 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3344 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3349 /* Send an event to our sinks until one of them works; don't then send to the
3350 * remaining sinks (unlike GstBin)
3351 * Special case: If a text sink is set we need to send the event
3352 * to them in case it's source is different from the a/v stream's source.
3355 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3357 gboolean res = TRUE;
3359 if (playsink->textchain && playsink->textchain->sink) {
3360 gst_event_ref (event);
3361 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3362 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3364 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3368 if (playsink->videochain) {
3369 gst_event_ref (event);
3370 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3371 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3374 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3376 if (playsink->audiochain) {
3377 gst_event_ref (event);
3378 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3379 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3382 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3386 gst_event_unref (event);
3390 /* We only want to send the event to a single sink (overriding GstBin's
3391 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3392 * events appropriately. So, this is a messy duplication of code. */
3394 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3396 gboolean res = FALSE;
3397 GstEventType event_type = GST_EVENT_TYPE (event);
3398 GstPlaySink *playsink;
3400 playsink = GST_PLAY_SINK_CAST (element);
3402 switch (event_type) {
3403 case GST_EVENT_SEEK:
3404 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3405 res = gst_play_sink_send_event_to_sink (playsink, event);
3407 case GST_EVENT_STEP:
3412 gboolean flush, intermediate;
3414 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3417 if (format == GST_FORMAT_BUFFERS) {
3418 /* for buffers, we will try to step video frames, for other formats we
3419 * send the step to all sinks */
3420 res = gst_play_sink_send_event_to_sink (playsink, event);
3423 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3430 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3437 static GstStateChangeReturn
3438 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3440 GstStateChangeReturn ret;
3441 GstStateChangeReturn bret;
3443 GstPlaySink *playsink;
3445 playsink = GST_PLAY_SINK (element);
3447 switch (transition) {
3448 case GST_STATE_CHANGE_READY_TO_PAUSED:
3449 playsink->need_async_start = TRUE;
3450 /* we want to go async to PAUSED until we managed to configure and add the
3452 do_async_start (playsink);
3453 ret = GST_STATE_CHANGE_ASYNC;
3455 case GST_STATE_CHANGE_PAUSED_TO_READY:
3456 /* unblock all pads here */
3457 GST_PLAY_SINK_LOCK (playsink);
3458 if (playsink->video_pad) {
3460 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3461 (playsink->video_pad)));
3462 if (gst_pad_is_blocked (opad)) {
3463 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3464 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3466 gst_object_unref (opad);
3467 playsink->video_pad_blocked = FALSE;
3470 if (playsink->audio_pad) {
3472 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3473 (playsink->audio_pad)));
3475 if (gst_pad_is_blocked (opad)) {
3476 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3477 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3479 gst_object_unref (opad);
3480 playsink->audio_pad_blocked = FALSE;
3483 if (playsink->text_pad) {
3485 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3486 (playsink->text_pad)));
3487 if (gst_pad_is_blocked (opad)) {
3488 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3489 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3491 gst_object_unref (opad);
3492 playsink->text_pad_blocked = FALSE;
3494 GST_PLAY_SINK_UNLOCK (playsink);
3496 case GST_STATE_CHANGE_READY_TO_NULL:
3497 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3498 /* remove our links to the mute and volume elements when they were
3499 * provided by a sink */
3500 disconnect_chain (playsink->audiochain, playsink);
3501 playsink->audiochain->volume = NULL;
3502 playsink->audiochain->mute = NULL;
3505 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3506 gst_object_unref (playsink->audiochain->ts_offset);
3507 playsink->audiochain->ts_offset = NULL;
3510 if (playsink->videochain && playsink->videochain->ts_offset) {
3511 gst_object_unref (playsink->videochain->ts_offset);
3512 playsink->videochain->ts_offset = NULL;
3514 ret = GST_STATE_CHANGE_SUCCESS;
3517 /* all other state changes return SUCCESS by default, this value can be
3518 * overridden by the result of the children */
3519 ret = GST_STATE_CHANGE_SUCCESS;
3523 /* do the state change of the children */
3525 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3527 /* now look at the result of our children and adjust the return value */
3529 case GST_STATE_CHANGE_FAILURE:
3530 /* failure, we stop */
3531 goto activate_failed;
3532 case GST_STATE_CHANGE_NO_PREROLL:
3533 /* some child returned NO_PREROLL. This is strange but we never know. We
3534 * commit our async state change (if any) and return the NO_PREROLL */
3535 do_async_done (playsink);
3538 case GST_STATE_CHANGE_ASYNC:
3539 /* some child was async, return this */
3543 /* return our previously configured return value */
3547 switch (transition) {
3548 case GST_STATE_CHANGE_READY_TO_PAUSED:
3550 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3551 /* FIXME Release audio device when we implement that */
3552 playsink->need_async_start = TRUE;
3554 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3555 if (playsink->video_sinkpad_stream_synchronizer) {
3556 gst_element_release_request_pad (GST_ELEMENT_CAST
3557 (playsink->stream_synchronizer),
3558 playsink->video_sinkpad_stream_synchronizer);
3559 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3560 playsink->video_sinkpad_stream_synchronizer = NULL;
3561 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3562 playsink->video_srcpad_stream_synchronizer = NULL;
3564 if (playsink->audio_sinkpad_stream_synchronizer) {
3565 gst_element_release_request_pad (GST_ELEMENT_CAST
3566 (playsink->stream_synchronizer),
3567 playsink->audio_sinkpad_stream_synchronizer);
3568 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3569 playsink->audio_sinkpad_stream_synchronizer = NULL;
3570 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3571 playsink->audio_srcpad_stream_synchronizer = NULL;
3573 if (playsink->text_sinkpad_stream_synchronizer) {
3574 gst_element_release_request_pad (GST_ELEMENT_CAST
3575 (playsink->stream_synchronizer),
3576 playsink->text_sinkpad_stream_synchronizer);
3577 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3578 playsink->text_sinkpad_stream_synchronizer = NULL;
3579 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3580 playsink->text_srcpad_stream_synchronizer = NULL;
3584 case GST_STATE_CHANGE_READY_TO_NULL:
3585 /* remove sinks we added */
3586 if (playsink->videodeinterlacechain) {
3587 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3589 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3591 if (playsink->videochain) {
3592 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3593 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3595 if (playsink->audiochain) {
3596 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3597 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3599 if (playsink->vischain) {
3600 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3601 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3603 if (playsink->textchain) {
3604 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3605 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3607 do_async_done (playsink);
3608 /* when going to READY, keep elements around as long as possible,
3609 * so they may be re-used faster next time/url around.
3610 * when really going to NULL, clean up everything completely. */
3611 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3613 /* Unparent the sinks to allow reuse */
3614 if (playsink->videochain && playsink->videochain->sink)
3615 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3616 playsink->videochain->sink);
3617 if (playsink->audiochain && playsink->audiochain->sink)
3618 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3619 playsink->audiochain->sink);
3620 if (playsink->textchain && playsink->textchain->sink)
3621 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3622 playsink->textchain->sink);
3624 if (playsink->audio_sink != NULL)
3625 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3626 if (playsink->video_sink != NULL)
3627 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3628 if (playsink->visualisation != NULL)
3629 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3630 if (playsink->text_sink != NULL)
3631 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3633 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3634 playsink->videodeinterlacechain = NULL;
3635 free_chain ((GstPlayChain *) playsink->videochain);
3636 playsink->videochain = NULL;
3637 free_chain ((GstPlayChain *) playsink->audiochain);
3638 playsink->audiochain = NULL;
3639 free_chain ((GstPlayChain *) playsink->vischain);
3640 playsink->vischain = NULL;
3641 free_chain ((GstPlayChain *) playsink->textchain);
3642 playsink->textchain = NULL;
3653 GST_DEBUG_OBJECT (element,
3654 "element failed to change states -- activation problem?");
3655 return GST_STATE_CHANGE_FAILURE;
3660 gst_play_sink_set_property (GObject * object, guint prop_id,
3661 const GValue * value, GParamSpec * spec)
3663 GstPlaySink *playsink = GST_PLAY_SINK (object);
3667 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3670 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3673 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3675 case PROP_FONT_DESC:
3676 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3678 case PROP_SUBTITLE_ENCODING:
3679 gst_play_sink_set_subtitle_encoding (playsink,
3680 g_value_get_string (value));
3682 case PROP_VIS_PLUGIN:
3683 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3685 case PROP_AV_OFFSET:
3686 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3688 case PROP_VIDEO_SINK:
3689 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3690 g_value_get_object (value));
3692 case PROP_AUDIO_SINK:
3693 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3694 g_value_get_object (value));
3696 case PROP_TEXT_SINK:
3697 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3698 g_value_get_object (value));
3701 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3707 gst_play_sink_get_property (GObject * object, guint prop_id,
3708 GValue * value, GParamSpec * spec)
3710 GstPlaySink *playsink = GST_PLAY_SINK (object);
3714 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3717 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3720 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3722 case PROP_FONT_DESC:
3723 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3725 case PROP_SUBTITLE_ENCODING:
3726 g_value_take_string (value,
3727 gst_play_sink_get_subtitle_encoding (playsink));
3729 case PROP_VIS_PLUGIN:
3730 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3733 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3735 case PROP_AV_OFFSET:
3736 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3738 case PROP_VIDEO_SINK:
3739 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3740 GST_PLAY_SINK_TYPE_VIDEO));
3742 case PROP_AUDIO_SINK:
3743 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3744 GST_PLAY_SINK_TYPE_AUDIO));
3746 case PROP_TEXT_SINK:
3747 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3748 GST_PLAY_SINK_TYPE_TEXT));
3751 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3758 gst_play_sink_plugin_init (GstPlugin * plugin)
3760 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3762 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3763 GST_TYPE_PLAY_SINK);