2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
26 * with newer GLib versions (>= 2.31.0) */
27 #define GLIB_DISABLE_DEPRECATION_WARNINGS
32 #include <gst/gst-i18n-plugin.h>
33 #include <gst/pbutils/pbutils.h>
34 #include <gst/video/video.h>
36 #include "gstplaysink.h"
37 #include "gststreamsynchronizer.h"
38 #include "gstplaysinkvideoconvert.h"
39 #include "gstplaysinkaudioconvert.h"
41 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
42 #define GST_CAT_DEFAULT gst_play_sink_debug
44 #define VOLUME_MAX_DOUBLE 10.0
46 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
47 GST_PLAY_FLAG_SOFT_VOLUME
49 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
51 /* holds the common data fields for the audio and video pipelines. We keep them
52 * in a structure to more easily have all the info available. */
55 GstPlaySink *playsink;
68 GstElement *volume; /* element with the volume property */
69 gboolean sink_volume; /* if the volume was provided by the sink */
70 GstElement *mute; /* element with the mute property */
72 GstElement *ts_offset;
78 GstPad *sinkpad, *srcpad;
80 GstElement *deinterlace;
81 } GstPlayVideoDeinterlaceChain;
91 GstElement *ts_offset;
100 GstElement *resample;
101 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
102 GstPad *vissinkpad; /* visualisation sinkpad, */
104 GstPad *vissrcpad; /* visualisation srcpad, */
105 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
114 GstElement *identity;
116 GstPad *videosinkpad;
118 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
120 GstElement *sink; /* custom sink to receive subtitle buffers */
123 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
124 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
125 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
126 g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
127 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
129 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
130 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
131 g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
134 #define PENDING_FLAG_SET(playsink, flagtype) \
135 ((playsink->pending_blocked_pads) |= (1 << flagtype))
136 #define PENDING_FLAG_UNSET(playsink, flagtype) \
137 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
138 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
139 ((playsink->pending_blocked_pads) & (1 << flagtype))
140 #define PENDING_VIDEO_BLOCK(playsink) \
141 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
142 #define PENDING_AUDIO_BLOCK(playsink) \
143 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
144 #define PENDING_TEXT_BLOCK(playsink) \
145 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
151 GStaticRecMutex lock;
153 gboolean async_pending;
154 gboolean need_async_start;
158 GstStreamSynchronizer *stream_synchronizer;
161 GstPlayAudioChain *audiochain;
162 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
163 GstPlayVideoChain *videochain;
164 GstPlayVisChain *vischain;
165 GstPlayTextChain *textchain;
169 gboolean audio_pad_raw;
170 gboolean audio_pad_blocked;
171 GstPad *audio_srcpad_stream_synchronizer;
172 GstPad *audio_sinkpad_stream_synchronizer;
174 GstElement *audio_tee;
175 GstPad *audio_tee_sink;
176 GstPad *audio_tee_asrc;
177 GstPad *audio_tee_vissrc;
180 gboolean video_pad_raw;
181 gboolean video_pad_blocked;
182 GstPad *video_srcpad_stream_synchronizer;
183 GstPad *video_sinkpad_stream_synchronizer;
186 gboolean text_pad_blocked;
187 GstPad *text_srcpad_stream_synchronizer;
188 GstPad *text_sinkpad_stream_synchronizer;
190 guint32 pending_blocked_pads;
193 GstElement *audio_sink;
194 GstElement *video_sink;
195 GstElement *visualisation;
196 GstElement *text_sink;
199 gchar *font_desc; /* font description */
200 gchar *subtitle_encoding; /* subtitle encoding */
201 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
203 gboolean volume_changed; /* volume/mute changed while no audiochain */
204 gboolean mute_changed; /* ... has been created yet */
208 struct _GstPlaySinkClass
210 GstBinClass parent_class;
212 gboolean (*reconfigure) (GstPlaySink * playsink);
214 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
218 static GstStaticPadTemplate audiotemplate =
219 GST_STATIC_PAD_TEMPLATE ("audio_sink",
222 GST_STATIC_CAPS_ANY);
223 static GstStaticPadTemplate videotemplate =
224 GST_STATIC_PAD_TEMPLATE ("video_sink",
227 GST_STATIC_CAPS_ANY);
228 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
231 GST_STATIC_CAPS_ANY);
233 /* FIXME 0.11: Remove */
234 static GstStaticPadTemplate audiorawtemplate =
235 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
238 GST_STATIC_CAPS_ANY);
239 static GstStaticPadTemplate videorawtemplate =
240 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
243 GST_STATIC_CAPS_ANY);
254 PROP_SUBTITLE_ENCODING,
270 static void gst_play_sink_dispose (GObject * object);
271 static void gst_play_sink_finalize (GObject * object);
272 static void gst_play_sink_set_property (GObject * object, guint prop_id,
273 const GValue * value, GParamSpec * spec);
274 static void gst_play_sink_get_property (GObject * object, guint prop_id,
275 GValue * value, GParamSpec * spec);
277 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
278 GstPadTemplate * templ, const gchar * name);
279 static void gst_play_sink_release_request_pad (GstElement * element,
281 static gboolean gst_play_sink_send_event (GstElement * element,
283 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
284 GstStateChange transition);
286 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
288 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
289 GstPlaySink * playsink);
290 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
291 GstPlaySink * playsink);
293 static void update_av_offset (GstPlaySink * playsink);
296 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
297 GValue * return_value G_GNUC_UNUSED,
298 guint n_param_values,
299 const GValue * param_values,
300 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
302 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
303 gpointer arg_1, gpointer data2);
304 register GMarshalFunc_OBJECT__BOXED callback;
305 register GCClosure *cc = (GCClosure *) closure;
306 register gpointer data1, data2;
308 g_return_if_fail (return_value != NULL);
309 g_return_if_fail (n_param_values == 2);
311 if (G_CCLOSURE_SWAP_DATA (closure)) {
312 data1 = closure->data;
313 data2 = g_value_peek_pointer (param_values + 0);
315 data1 = g_value_peek_pointer (param_values + 0);
316 data2 = closure->data;
319 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
321 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
323 gst_value_take_buffer (return_value, v_return);
326 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
328 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
331 gst_play_sink_class_init (GstPlaySinkClass * klass)
333 GObjectClass *gobject_klass;
334 GstElementClass *gstelement_klass;
335 GstBinClass *gstbin_klass;
337 gobject_klass = (GObjectClass *) klass;
338 gstelement_klass = (GstElementClass *) klass;
339 gstbin_klass = (GstBinClass *) klass;
341 gobject_klass->dispose = gst_play_sink_dispose;
342 gobject_klass->finalize = gst_play_sink_finalize;
343 gobject_klass->set_property = gst_play_sink_set_property;
344 gobject_klass->get_property = gst_play_sink_get_property;
350 * Control the behaviour of playsink.
352 g_object_class_install_property (gobject_klass, PROP_FLAGS,
353 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
354 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
355 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
358 * GstPlaySink:volume:
360 * Get or set the current audio stream volume. 1.0 means 100%,
361 * 0.0 means mute. This uses a linear volume scale.
364 g_object_class_install_property (gobject_klass, PROP_VOLUME,
365 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
366 0.0, VOLUME_MAX_DOUBLE, 1.0,
367 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
368 g_object_class_install_property (gobject_klass, PROP_MUTE,
369 g_param_spec_boolean ("mute", "Mute",
370 "Mute the audio channel without changing the volume", FALSE,
371 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
372 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
373 g_param_spec_string ("subtitle-font-desc",
374 "Subtitle font description",
375 "Pango font description of font "
376 "to be used for subtitle rendering", NULL,
377 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
378 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
379 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
380 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
381 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
382 "be checked for an encoding to use. If that is not set either, "
383 "ISO-8859-15 will be assumed.", NULL,
384 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
385 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
386 g_param_spec_object ("vis-plugin", "Vis plugin",
387 "the visualization element to use (NULL = default)",
388 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
392 * Get the currently rendered or prerolled frame in the video sink.
393 * The #GstCaps on the buffer will describe the format of the buffer.
397 g_object_class_install_property (gobject_klass, PROP_FRAME,
398 gst_param_spec_mini_object ("frame", "Frame",
399 "The last frame (NULL = no video available)",
400 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
402 * GstPlaySink:av-offset:
404 * Control the synchronisation offset between the audio and video streams.
405 * Positive values make the audio ahead of the video and negative values make
406 * the audio go behind the video.
410 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
411 g_param_spec_int64 ("av-offset", "AV Offset",
412 "The synchronisation offset between audio and video in nanoseconds",
413 G_MININT64, G_MAXINT64, 0,
414 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
417 * GstPlaySink:video-sink:
419 * Set the used video sink element. NULL will use the default sink. playsink
420 * must be in %GST_STATE_NULL
424 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
425 g_param_spec_object ("video-sink", "Video Sink",
426 "the video output element to use (NULL = default sink)",
427 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
429 * GstPlaySink:audio-sink:
431 * Set the used audio 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_AUDIO_SINK,
437 g_param_spec_object ("audio-sink", "Audio Sink",
438 "the audio output element to use (NULL = default sink)",
439 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
441 * GstPlaySink:text-sink:
443 * Set the used text 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_TEXT_SINK,
449 g_param_spec_object ("text-sink", "Text sink",
450 "the text output element to use (NULL = default textoverlay)",
451 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
454 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
455 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
456 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
459 * GstPlaySink::convert-frame
460 * @playsink: a #GstPlaySink
461 * @caps: the target format of the frame
463 * Action signal to retrieve the currently playing video frame in the format
464 * specified by @caps.
465 * If @caps is %NULL, no conversion will be performed and this function is
466 * equivalent to the #GstPlaySink::frame property.
468 * Returns: a #GstBuffer of the current video frame converted to #caps.
469 * The caps on the buffer will describe the final layout of the buffer data.
470 * %NULL is returned when no current buffer can be retrieved or when the
475 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
476 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
477 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
478 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
480 gst_element_class_add_static_pad_template (gstelement_klass,
482 gst_element_class_add_static_pad_template (gstelement_klass,
484 gst_element_class_add_static_pad_template (gstelement_klass,
486 gst_element_class_add_static_pad_template (gstelement_klass,
488 gst_element_class_add_static_pad_template (gstelement_klass,
490 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
492 "Convenience sink for multiple streams",
493 "Wim Taymans <wim.taymans@gmail.com>");
495 gstelement_klass->change_state =
496 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
497 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
498 gstelement_klass->request_new_pad =
499 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
500 gstelement_klass->release_pad =
501 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
503 gstbin_klass->handle_message =
504 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
506 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
507 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
511 gst_play_sink_init (GstPlaySink * playsink)
514 playsink->video_sink = NULL;
515 playsink->audio_sink = NULL;
516 playsink->visualisation = NULL;
517 playsink->text_sink = NULL;
518 playsink->volume = 1.0;
519 playsink->font_desc = NULL;
520 playsink->subtitle_encoding = NULL;
521 playsink->flags = DEFAULT_FLAGS;
523 playsink->stream_synchronizer =
524 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
525 gst_bin_add (GST_BIN_CAST (playsink),
526 GST_ELEMENT_CAST (playsink->stream_synchronizer));
528 g_static_rec_mutex_init (&playsink->lock);
529 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
533 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
537 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
540 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
546 free_chain (GstPlayChain * chain)
550 gst_object_unref (chain->bin);
556 gst_play_sink_dispose (GObject * object)
558 GstPlaySink *playsink;
560 playsink = GST_PLAY_SINK (object);
562 if (playsink->audio_sink != NULL) {
563 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
564 gst_object_unref (playsink->audio_sink);
565 playsink->audio_sink = NULL;
567 if (playsink->video_sink != NULL) {
568 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
569 gst_object_unref (playsink->video_sink);
570 playsink->video_sink = NULL;
572 if (playsink->visualisation != NULL) {
573 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
574 gst_object_unref (playsink->visualisation);
575 playsink->visualisation = NULL;
577 if (playsink->text_sink != NULL) {
578 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
579 gst_object_unref (playsink->text_sink);
580 playsink->text_sink = NULL;
583 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
584 playsink->videodeinterlacechain = NULL;
585 free_chain ((GstPlayChain *) playsink->videochain);
586 playsink->videochain = NULL;
587 free_chain ((GstPlayChain *) playsink->audiochain);
588 playsink->audiochain = NULL;
589 free_chain ((GstPlayChain *) playsink->vischain);
590 playsink->vischain = NULL;
591 free_chain ((GstPlayChain *) playsink->textchain);
592 playsink->textchain = NULL;
594 if (playsink->audio_tee_sink) {
595 gst_object_unref (playsink->audio_tee_sink);
596 playsink->audio_tee_sink = NULL;
599 if (playsink->audio_tee_vissrc) {
600 gst_element_release_request_pad (playsink->audio_tee,
601 playsink->audio_tee_vissrc);
602 gst_object_unref (playsink->audio_tee_vissrc);
603 playsink->audio_tee_vissrc = NULL;
606 if (playsink->audio_tee_asrc) {
607 gst_element_release_request_pad (playsink->audio_tee,
608 playsink->audio_tee_asrc);
609 gst_object_unref (playsink->audio_tee_asrc);
610 playsink->audio_tee_asrc = NULL;
613 g_free (playsink->font_desc);
614 playsink->font_desc = NULL;
616 g_free (playsink->subtitle_encoding);
617 playsink->subtitle_encoding = NULL;
619 playsink->stream_synchronizer = NULL;
621 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
625 gst_play_sink_finalize (GObject * object)
627 GstPlaySink *playsink;
629 playsink = GST_PLAY_SINK (object);
631 g_static_rec_mutex_free (&playsink->lock);
633 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
637 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
640 GstElement **elem = NULL, *old = NULL;
642 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
644 GST_PLAY_SINK_LOCK (playsink);
646 case GST_PLAY_SINK_TYPE_AUDIO:
647 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
648 elem = &playsink->audio_sink;
650 case GST_PLAY_SINK_TYPE_VIDEO:
651 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
652 elem = &playsink->video_sink;
654 case GST_PLAY_SINK_TYPE_TEXT:
655 elem = &playsink->text_sink;
663 gst_object_ref (sink);
666 GST_PLAY_SINK_UNLOCK (playsink);
670 gst_element_set_state (old, GST_STATE_NULL);
671 gst_object_unref (old);
676 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
678 GstElement *result = NULL;
679 GstElement *elem = NULL, *chainp = NULL;
681 GST_PLAY_SINK_LOCK (playsink);
683 case GST_PLAY_SINK_TYPE_AUDIO:
684 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
686 GstPlayAudioChain *chain;
687 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
688 chainp = chain->sink;
689 elem = playsink->audio_sink;
692 case GST_PLAY_SINK_TYPE_VIDEO:
693 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
695 GstPlayVideoChain *chain;
696 if ((chain = (GstPlayVideoChain *) playsink->videochain))
697 chainp = chain->sink;
698 elem = playsink->video_sink;
701 case GST_PLAY_SINK_TYPE_TEXT:
703 GstPlayTextChain *chain;
704 if ((chain = (GstPlayTextChain *) playsink->textchain))
705 chainp = chain->sink;
706 elem = playsink->text_sink;
713 /* we have an active chain with a sink, get the sink */
714 result = gst_object_ref (chainp);
716 /* nothing found, return last configured sink */
717 if (result == NULL && elem)
718 result = gst_object_ref (elem);
719 GST_PLAY_SINK_UNLOCK (playsink);
725 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
728 GstPlaySink *playsink;
730 playsink = GST_PLAY_SINK (user_data);
731 /* nothing to do here, we need a dummy callback here to make the async call
733 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
737 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
740 GstPlaySink *playsink;
741 GstPlayVisChain *chain;
743 playsink = GST_PLAY_SINK (user_data);
745 GST_PLAY_SINK_LOCK (playsink);
746 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
747 /* now try to change the plugin in the running vis chain */
748 if (!(chain = (GstPlayVisChain *) playsink->vischain))
751 /* unlink the old plugin and unghost the pad */
752 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
753 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
755 /* set the old plugin to NULL and remove */
756 gst_element_set_state (chain->vis, GST_STATE_NULL);
757 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
759 /* add new plugin and set state to playing */
760 chain->vis = playsink->visualisation;
761 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
762 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
765 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
766 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
769 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
770 GST_PAD_LINK_CHECK_NOTHING);
771 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
775 /* Unblock the pad */
776 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
778 GST_PLAY_SINK_UNLOCK (playsink);
782 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
784 GstPlayVisChain *chain;
786 /* setting NULL means creating the default vis plugin */
788 vis = gst_element_factory_make ("goom", "vis");
790 /* simply return if we don't have a vis plugin here */
794 GST_PLAY_SINK_LOCK (playsink);
795 /* first store the new vis */
796 if (playsink->visualisation)
797 gst_object_unref (playsink->visualisation);
799 gst_object_ref_sink (vis);
800 playsink->visualisation = vis;
802 /* now try to change the plugin in the running vis chain, if we have no chain,
803 * we don't bother, any future vis chain will be created with the new vis
805 if (!(chain = (GstPlayVisChain *) playsink->vischain))
808 /* block the pad, the next time the callback is called we can change the
809 * visualisation. It's possible that this never happens or that the pad was
810 * already blocked. If the callback never happens, we don't have new data so
811 * we don't need the new vis plugin. If the pad was already blocked, the
812 * function returns FALSE but the previous pad block will do the right thing
814 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
815 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
818 GST_PLAY_SINK_UNLOCK (playsink);
824 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
826 GstElement *result = NULL;
827 GstPlayVisChain *chain;
829 GST_PLAY_SINK_LOCK (playsink);
830 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
831 /* we have an active chain, get the sink */
833 result = gst_object_ref (chain->vis);
835 /* nothing found, return last configured sink */
836 if (result == NULL && playsink->visualisation)
837 result = gst_object_ref (playsink->visualisation);
838 GST_PLAY_SINK_UNLOCK (playsink);
844 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
846 GstPlayAudioChain *chain;
848 GST_PLAY_SINK_LOCK (playsink);
849 playsink->volume = volume;
850 chain = (GstPlayAudioChain *) playsink->audiochain;
851 if (chain && chain->volume) {
852 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
853 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
854 chain->mute, volume, playsink->mute);
855 /* if there is a mute element or we are not muted, set the volume */
856 if (chain->mute || !playsink->mute)
857 g_object_set (chain->volume, "volume", volume, NULL);
859 GST_LOG_OBJECT (playsink, "no volume element");
860 playsink->volume_changed = TRUE;
862 GST_PLAY_SINK_UNLOCK (playsink);
866 gst_play_sink_get_volume (GstPlaySink * playsink)
869 GstPlayAudioChain *chain;
871 GST_PLAY_SINK_LOCK (playsink);
872 chain = (GstPlayAudioChain *) playsink->audiochain;
873 result = playsink->volume;
874 if (chain && chain->volume) {
875 if (chain->mute || !playsink->mute) {
876 g_object_get (chain->volume, "volume", &result, NULL);
877 playsink->volume = result;
880 GST_PLAY_SINK_UNLOCK (playsink);
886 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
888 GstPlayAudioChain *chain;
890 GST_PLAY_SINK_LOCK (playsink);
891 playsink->mute = mute;
892 chain = (GstPlayAudioChain *) playsink->audiochain;
895 g_object_set (chain->mute, "mute", mute, NULL);
896 } else if (chain->volume) {
898 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
900 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
904 playsink->mute_changed = TRUE;
906 GST_PLAY_SINK_UNLOCK (playsink);
910 gst_play_sink_get_mute (GstPlaySink * playsink)
913 GstPlayAudioChain *chain;
915 GST_PLAY_SINK_LOCK (playsink);
916 chain = (GstPlayAudioChain *) playsink->audiochain;
917 if (chain && chain->mute) {
918 g_object_get (chain->mute, "mute", &result, NULL);
919 playsink->mute = result;
921 result = playsink->mute;
923 GST_PLAY_SINK_UNLOCK (playsink);
929 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
933 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
934 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
938 add_chain (GstPlayChain * chain, gboolean add)
940 if (chain->added == add)
944 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
946 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
947 /* we don't want to lose our sink status */
948 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
957 activate_chain (GstPlayChain * chain, gboolean activate)
961 if (chain->activated == activate)
964 GST_OBJECT_LOCK (chain->playsink);
965 state = GST_STATE_TARGET (chain->playsink);
966 GST_OBJECT_UNLOCK (chain->playsink);
969 gst_element_set_state (chain->bin, state);
971 gst_element_set_state (chain->bin, GST_STATE_NULL);
973 chain->activated = activate;
979 element_is_sink (GstElement * element)
983 GST_OBJECT_LOCK (element);
984 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
985 GST_OBJECT_UNLOCK (element);
987 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
992 element_has_property (GstElement * element, const gchar * pname, GType type)
996 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
999 GST_DEBUG_OBJECT (element, "no %s property", pname);
1003 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1004 g_type_is_a (pspec->value_type, type)) {
1005 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1006 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1010 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1011 "and we expected it to be of type %s", pname,
1012 g_type_name (pspec->value_type), g_type_name (type));
1019 const gchar *prop_name;
1022 } FindPropertyHelper;
1025 find_property (GstElement * element, FindPropertyHelper * helper)
1027 if (helper->need_sink && !element_is_sink (element)) {
1028 gst_object_unref (element);
1032 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1033 gst_object_unref (element);
1037 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1038 (helper->need_sink) ? "sink" : "element");
1039 return 0; /* keep it */
1042 /* FIXME: why not move these functions into core? */
1043 /* find a sink in the hierarchy with a property named @name. This function does
1044 * not increase the refcount of the returned object and thus remains valid as
1045 * long as the bin is valid. */
1047 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1048 const gchar * name, GType expected_type)
1050 GstElement *result = NULL;
1053 if (element_has_property (obj, name, expected_type)) {
1055 } else if (GST_IS_BIN (obj)) {
1056 FindPropertyHelper helper = { name, expected_type, TRUE };
1058 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1059 result = gst_iterator_find_custom (it,
1060 (GCompareFunc) find_property, &helper);
1061 gst_iterator_free (it);
1062 /* we don't need the extra ref */
1064 gst_object_unref (result);
1069 /* find an object in the hierarchy with a property named @name */
1071 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1072 const gchar * name, GType expected_type)
1074 GstElement *result = NULL;
1077 if (GST_IS_BIN (obj)) {
1078 FindPropertyHelper helper = { name, expected_type, FALSE };
1080 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1081 result = gst_iterator_find_custom (it,
1082 (GCompareFunc) find_property, &helper);
1083 gst_iterator_free (it);
1085 if (element_has_property (obj, name, expected_type)) {
1087 gst_object_ref (obj);
1094 do_async_start (GstPlaySink * playsink)
1096 GstMessage *message;
1098 if (!playsink->need_async_start) {
1099 GST_INFO_OBJECT (playsink, "no async_start needed");
1103 playsink->async_pending = TRUE;
1105 GST_INFO_OBJECT (playsink, "Sending async_start message");
1106 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1107 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1108 (playsink), message);
1112 do_async_done (GstPlaySink * playsink)
1114 GstMessage *message;
1116 if (playsink->async_pending) {
1117 GST_INFO_OBJECT (playsink, "Sending async_done message");
1118 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1119 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1120 (playsink), message);
1122 playsink->async_pending = FALSE;
1125 playsink->need_async_start = FALSE;
1128 /* try to change the state of an element. This function returns the element when
1129 * the state change could be performed. When this function returns NULL an error
1130 * occured and the element is unreffed if @unref is TRUE. */
1132 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1134 GstStateChangeReturn ret;
1137 ret = gst_element_set_state (element, GST_STATE_READY);
1138 if (ret == GST_STATE_CHANGE_FAILURE) {
1139 GST_DEBUG_OBJECT (playsink, "failed state change..");
1140 gst_element_set_state (element, GST_STATE_NULL);
1142 gst_object_unref (element);
1149 /* make the element (bin) that contains the elements needed to perform
1150 * video display. Only used for *raw* video streams.
1152 * +------------------------------------------------------------+
1154 * | +-------+ +----------+ +----------+ +---------+ |
1155 * | | queue | |colorspace| |videoscale| |videosink| |
1156 * | +-sink src-sink src-sink src-sink | |
1157 * | | +-------+ +----------+ +----------+ +---------+ |
1159 * +------------------------------------------------------------+
1162 static GstPlayVideoDeinterlaceChain *
1163 gen_video_deinterlace_chain (GstPlaySink * playsink)
1165 GstPlayVideoDeinterlaceChain *chain;
1168 GstElement *head = NULL, *prev = NULL;
1170 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1171 chain->chain.playsink = playsink;
1173 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1175 /* create a bin to hold objects, as we create them we add them to this bin so
1176 * that when something goes wrong we only need to unref the bin */
1177 chain->chain.bin = gst_bin_new ("vdbin");
1178 bin = GST_BIN_CAST (chain->chain.bin);
1179 gst_object_ref_sink (bin);
1181 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1182 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1183 if (chain->conv == NULL) {
1184 post_missing_element_message (playsink, COLORSPACE);
1185 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1186 (_("Missing element '%s' - check your GStreamer installation."),
1187 COLORSPACE), ("video rendering might fail"));
1189 gst_bin_add (bin, chain->conv);
1194 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1195 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1196 if (chain->deinterlace == NULL) {
1197 post_missing_element_message (playsink, "deinterlace");
1198 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1199 (_("Missing element '%s' - check your GStreamer installation."),
1200 "deinterlace"), ("deinterlacing won't work"));
1202 gst_bin_add (bin, chain->deinterlace);
1204 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1205 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1208 head = chain->deinterlace;
1210 prev = chain->deinterlace;
1214 pad = gst_element_get_static_pad (head, "sink");
1215 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1216 gst_object_unref (pad);
1218 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1222 pad = gst_element_get_static_pad (prev, "src");
1223 chain->srcpad = gst_ghost_pad_new ("src", pad);
1224 gst_object_unref (pad);
1226 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1229 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1230 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1236 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1237 (NULL), ("Failed to configure the video deinterlace chain."));
1238 free_chain ((GstPlayChain *) chain);
1243 #ifdef GST_EXT_LINK_FIMCCONVERT
1245 compare_name (GstElement * element, const gchar * name)
1248 GST_OBJECT_LOCK (element);
1249 str = g_strrstr(GST_ELEMENT_NAME (element), name);
1250 GST_OBJECT_UNLOCK (element);
1259 /* make the element (bin) that contains the elements needed to perform
1262 * +------------------------------------------------------------+
1264 * | +-------+ +----------+ +----------+ +---------+ |
1265 * | | queue | |colorspace| |videoscale| |videosink| |
1266 * | +-sink src-sink src-sink src-sink | |
1267 * | | +-------+ +----------+ +----------+ +---------+ |
1269 * +------------------------------------------------------------+
1272 static GstPlayVideoChain *
1273 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1275 GstPlayVideoChain *chain;
1278 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1279 GstPlugin *p = NULL;
1281 chain = g_new0 (GstPlayVideoChain, 1);
1282 chain->chain.playsink = playsink;
1283 chain->chain.raw = raw;
1285 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1287 if (playsink->video_sink) {
1288 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1289 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1291 /* only try fallback if no specific sink was chosen */
1292 if (chain->sink == NULL) {
1293 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1294 elem = gst_element_factory_make ("autovideosink", "videosink");
1295 chain->sink = try_element (playsink, elem, TRUE);
1297 if (chain->sink == NULL) {
1298 /* if default sink from config.h is different then try it too */
1299 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1300 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1301 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1302 chain->sink = try_element (playsink, elem, TRUE);
1306 playsink->video_sink = gst_object_ref (chain->sink);
1308 if (chain->sink == NULL)
1312 /* if we can disable async behaviour of the sink, we can avoid adding a
1313 * queue for the audio chain. */
1315 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1318 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1319 async, GST_ELEMENT_NAME (elem));
1320 g_object_set (elem, "async", async, NULL);
1321 chain->async = async;
1323 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1324 chain->async = TRUE;
1327 /* find ts-offset element */
1328 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1329 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1332 /* create a bin to hold objects, as we create them we add them to this bin so
1333 * that when something goes wrong we only need to unref the bin */
1334 chain->chain.bin = gst_bin_new ("vbin");
1335 bin = GST_BIN_CAST (chain->chain.bin);
1336 gst_object_ref_sink (bin);
1337 gst_bin_add (bin, chain->sink);
1339 #ifndef GST_EXT_XV_ENHANCEMENT
1340 /* decouple decoder from sink, this improves playback quite a lot since the
1341 * decoder can continue while the sink blocks for synchronisation. We don't
1342 * need a lot of buffers as this consumes a lot of memory and we don't want
1343 * too little because else we would be context switching too quickly. */
1344 chain->queue = gst_element_factory_make ("queue", "vqueue");
1345 if (chain->queue == NULL) {
1346 post_missing_element_message (playsink, "queue");
1347 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1348 (_("Missing element '%s' - check your GStreamer installation."),
1349 "queue"), ("video rendering might be suboptimal"));
1353 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1354 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1355 gst_bin_add (bin, chain->queue);
1356 head = prev = chain->queue;
1360 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1361 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1362 #ifdef GST_EXT_LINK_FIMCCONVERT
1363 GstIterator *iter = gst_bin_iterate_recurse (gst_element_get_parent(playsink));
1364 gpointer e = gst_iterator_find_custom (iter, (GCompareFunc) compare_name, "omx");
1365 gst_iterator_free (iter);
1367 chain->conv = gst_element_factory_make ("fimcconvert", "vconv");
1368 gst_object_unref(p);
1370 chain->conv = gst_element_factory_make (COLORSPACE, "vconv");
1372 chain->conv = g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1374 gst_bin_add (bin, chain->conv);
1376 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1377 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1386 GST_DEBUG_OBJECT (playsink, "linking to sink");
1387 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1388 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1392 pad = gst_element_get_static_pad (head, "sink");
1393 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1394 gst_object_unref (pad);
1396 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1403 if (!elem && !playsink->video_sink) {
1404 post_missing_element_message (playsink, "autovideosink");
1405 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1406 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1407 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1408 (_("Both autovideosink and %s elements are missing."),
1409 DEFAULT_VIDEOSINK), (NULL));
1411 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1412 (_("The autovideosink element is missing.")), (NULL));
1415 if (playsink->video_sink) {
1416 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1417 (_("Configured videosink %s is not working."),
1418 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1419 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1420 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1421 (_("Both autovideosink and %s elements are not working."),
1422 DEFAULT_VIDEOSINK), (NULL));
1424 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1425 (_("The autovideosink element is not working.")), (NULL));
1428 free_chain ((GstPlayChain *) chain);
1433 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1434 (NULL), ("Failed to configure the video sink."));
1435 /* checking sink made it READY */
1436 gst_element_set_state (chain->sink, GST_STATE_NULL);
1437 /* Remove chain from the bin to allow reuse later */
1438 gst_bin_remove (bin, chain->sink);
1439 free_chain ((GstPlayChain *) chain);
1445 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1448 GstPlayVideoChain *chain;
1449 GstStateChangeReturn ret;
1451 chain = playsink->videochain;
1453 chain->chain.raw = raw;
1455 /* if the chain was active we don't do anything */
1456 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1459 /* try to set the sink element to READY again */
1460 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1461 if (ret == GST_STATE_CHANGE_FAILURE)
1464 /* find ts-offset element */
1466 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1467 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1470 /* if we can disable async behaviour of the sink, we can avoid adding a
1471 * queue for the audio chain. */
1473 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1476 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1477 async, GST_ELEMENT_NAME (elem));
1478 g_object_set (elem, "async", async, NULL);
1479 chain->async = async;
1481 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1482 chain->async = TRUE;
1487 /* make an element for playback of video with subtitles embedded.
1488 * Only used for *raw* video streams.
1490 * +--------------------------------------------+
1492 * | +--------+ +-----------------+ |
1493 * | | queue | | subtitleoverlay | |
1494 * video--src sink---video_sink | |
1495 * | +--------+ | src--src
1496 * text------------------text_sink | |
1497 * | +-----------------+ |
1498 * +--------------------------------------------+
1501 static GstPlayTextChain *
1502 gen_text_chain (GstPlaySink * playsink)
1504 GstPlayTextChain *chain;
1507 GstPad *videosinkpad, *textsinkpad, *srcpad;
1509 chain = g_new0 (GstPlayTextChain, 1);
1510 chain->chain.playsink = playsink;
1512 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1514 chain->chain.bin = gst_bin_new ("tbin");
1515 bin = GST_BIN_CAST (chain->chain.bin);
1516 gst_object_ref_sink (bin);
1518 videosinkpad = textsinkpad = srcpad = NULL;
1520 /* first try to hook the text pad to the custom sink */
1521 if (playsink->text_sink) {
1522 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1523 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1526 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1529 /* make sure the sparse subtitles don't participate in the preroll */
1530 g_object_set (elem, "async", FALSE, NULL);
1531 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1532 gst_bin_add (bin, chain->sink);
1533 /* NOTE streamsynchronizer needs streams decoupled */
1534 /* make a little queue */
1535 chain->queue = gst_element_factory_make ("queue", "subqueue");
1536 if (chain->queue == NULL) {
1537 post_missing_element_message (playsink, "queue");
1538 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1539 (_("Missing element '%s' - check your GStreamer installation."),
1540 "queue"), ("rendering might be suboptimal"));
1542 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1543 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1544 "silent", TRUE, NULL);
1545 gst_bin_add (bin, chain->queue);
1547 /* we have a custom sink, this will be our textsinkpad */
1548 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1549 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1550 /* we're all fine now and we can add the sink to the chain */
1551 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1552 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1554 GST_WARNING_OBJECT (playsink,
1555 "can't find a sink pad on custom text sink");
1556 gst_bin_remove (bin, chain->sink);
1557 gst_bin_remove (bin, chain->queue);
1559 chain->queue = NULL;
1561 /* try to set sync to true but it's no biggie when we can't */
1562 if (chain->sink && (elem =
1563 gst_play_sink_find_property_sinks (playsink, chain->sink,
1564 "sync", G_TYPE_BOOLEAN)))
1565 g_object_set (elem, "sync", TRUE, NULL);
1568 gst_bin_remove (bin, chain->sink);
1570 GST_WARNING_OBJECT (playsink,
1571 "can't find async property in custom text sink");
1574 if (textsinkpad == NULL) {
1575 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1576 (_("Custom text sink element is not usable.")),
1577 ("fallback to default textoverlay"));
1581 if (textsinkpad == NULL) {
1582 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1583 /* make a little queue */
1584 chain->queue = gst_element_factory_make ("queue", "vqueue");
1585 if (chain->queue == NULL) {
1586 post_missing_element_message (playsink, "queue");
1587 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1588 (_("Missing element '%s' - check your GStreamer installation."),
1589 "queue"), ("video rendering might be suboptimal"));
1591 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1592 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1593 "silent", TRUE, NULL);
1594 gst_bin_add (bin, chain->queue);
1595 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1599 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1600 if (chain->overlay == NULL) {
1601 post_missing_element_message (playsink, "subtitleoverlay");
1602 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1603 (_("Missing element '%s' - check your GStreamer installation."),
1604 "subtitleoverlay"), ("subtitle rendering disabled"));
1606 GstElement *element;
1608 gst_bin_add (bin, chain->overlay);
1610 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1611 if (playsink->font_desc) {
1612 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1613 playsink->font_desc, NULL);
1615 if (playsink->subtitle_encoding) {
1616 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1617 playsink->subtitle_encoding, NULL);
1620 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1621 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1623 /* make another little queue to decouple streams */
1624 element = gst_element_factory_make ("queue", "subqueue");
1625 if (element == NULL) {
1626 post_missing_element_message (playsink, "queue");
1627 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1628 (_("Missing element '%s' - check your GStreamer installation."),
1629 "queue"), ("rendering might be suboptimal"));
1631 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1632 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1633 "silent", TRUE, NULL);
1634 gst_bin_add (bin, element);
1635 if (gst_element_link_pads_full (element, "src", chain->overlay,
1636 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1637 textsinkpad = gst_element_get_static_pad (element, "sink");
1638 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1640 gst_bin_remove (bin, chain->sink);
1641 gst_bin_remove (bin, chain->overlay);
1643 chain->overlay = NULL;
1644 gst_object_unref (videosinkpad);
1645 videosinkpad = NULL;
1652 if (videosinkpad == NULL) {
1653 /* if we still don't have a videosink, we don't have an overlay. the only
1654 * thing we can do is insert an identity and ghost the src
1656 chain->identity = gst_element_factory_make ("identity", "tidentity");
1657 if (chain->identity == NULL) {
1658 post_missing_element_message (playsink, "identity");
1659 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1660 (_("Missing element '%s' - check your GStreamer installation."),
1661 "identity"), (NULL));
1663 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1664 g_object_set (chain->identity, "silent", TRUE, NULL);
1665 gst_bin_add (bin, chain->identity);
1666 srcpad = gst_element_get_static_pad (chain->identity, "src");
1667 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1671 /* expose the ghostpads */
1673 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1674 gst_object_unref (videosinkpad);
1675 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1678 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1679 gst_object_unref (textsinkpad);
1680 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1683 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1684 gst_object_unref (srcpad);
1685 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1692 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1696 g_object_get (object, "volume", &vol, NULL);
1697 playsink->volume = vol;
1699 g_object_notify (G_OBJECT (playsink), "volume");
1703 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1707 g_object_get (object, "mute", &mute, NULL);
1708 playsink->mute = mute;
1710 g_object_notify (G_OBJECT (playsink), "mute");
1713 /* make the chain that contains the elements needed to perform
1716 * We add a tee as the first element so that we can link the visualisation chain
1717 * to it when requested.
1719 * +-------------------------------------------------------------+
1721 * | +---------+ +----------+ +---------+ +---------+ |
1722 * | |audioconv| |audioscale| | volume | |audiosink| |
1723 * | +-srck src-sink src-sink src-sink | |
1724 * | | +---------+ +----------+ +---------+ +---------+ |
1726 * +-------------------------------------------------------------+
1728 static GstPlayAudioChain *
1729 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1731 GstPlayAudioChain *chain;
1733 gboolean have_volume;
1735 GstElement *head, *prev, *elem = NULL;
1737 chain = g_new0 (GstPlayAudioChain, 1);
1738 chain->chain.playsink = playsink;
1739 chain->chain.raw = raw;
1741 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1743 if (playsink->audio_sink) {
1744 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1745 playsink->audio_sink);
1746 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1748 /* only try fallback if no specific sink was chosen */
1749 if (chain->sink == NULL) {
1750 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1751 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1752 chain->sink = try_element (playsink, elem, TRUE);
1754 if (chain->sink == NULL) {
1755 /* if default sink from config.h is different then try it too */
1756 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1757 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1758 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1759 chain->sink = try_element (playsink, elem, TRUE);
1763 playsink->audio_sink = gst_object_ref (chain->sink);
1765 if (chain->sink == NULL)
1768 chain->chain.bin = gst_bin_new ("abin");
1769 bin = GST_BIN_CAST (chain->chain.bin);
1770 gst_object_ref_sink (bin);
1771 gst_bin_add (bin, chain->sink);
1773 /* we have to add a queue when we need to decouple for the video sink in
1774 * visualisations and for streamsynchronizer */
1775 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1776 chain->queue = gst_element_factory_make ("queue", "aqueue");
1777 if (chain->queue == NULL) {
1778 post_missing_element_message (playsink, "queue");
1779 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1780 (_("Missing element '%s' - check your GStreamer installation."),
1781 "queue"), ("audio playback and visualizations might not work"));
1785 g_object_set (chain->queue, "silent", TRUE, NULL);
1786 gst_bin_add (bin, chain->queue);
1787 prev = head = chain->queue;
1790 /* find ts-offset element */
1791 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1792 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1795 /* check if the sink, or something within the sink, has the volume property.
1796 * If it does we don't need to add a volume element. */
1798 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1801 chain->volume = elem;
1803 g_signal_connect (chain->volume, "notify::volume",
1804 G_CALLBACK (notify_volume_cb), playsink);
1806 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1808 chain->sink_volume = TRUE;
1809 /* if the sink also has a mute property we can use this as well. We'll only
1810 * use the mute property if there is a volume property. We can simulate the
1811 * mute with the volume otherwise. */
1813 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1816 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1817 g_signal_connect (chain->mute, "notify::mute",
1818 G_CALLBACK (notify_mute_cb), playsink);
1820 /* use the sink to control the volume and mute */
1821 if (playsink->volume_changed) {
1822 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1823 playsink->volume_changed = FALSE;
1825 if (playsink->mute_changed) {
1827 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1830 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1832 playsink->mute_changed = FALSE;
1835 /* no volume, we need to add a volume element when we can */
1836 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1837 have_volume = FALSE;
1838 chain->sink_volume = FALSE;
1841 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1842 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1843 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1844 gboolean use_volume =
1845 !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME;
1846 GST_DEBUG_OBJECT (playsink,
1847 "creating audioconvert with use-converters %d, use-volume %d",
1848 use_converters, use_volume);
1850 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1851 "use-converters", use_converters, "use-volume", use_volume, NULL);
1852 gst_bin_add (bin, chain->conv);
1854 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1855 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1862 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1863 GstPlaySinkAudioConvert *conv =
1864 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1867 chain->volume = conv->volume;
1870 g_signal_connect (chain->volume, "notify::volume",
1871 G_CALLBACK (notify_volume_cb), playsink);
1873 /* volume also has the mute property */
1874 chain->mute = chain->volume;
1875 g_signal_connect (chain->mute, "notify::mute",
1876 G_CALLBACK (notify_mute_cb), playsink);
1878 /* configure with the latest volume and mute */
1879 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1881 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1887 /* we only have to link to the previous element if we have something in
1888 * front of the sink */
1889 GST_DEBUG_OBJECT (playsink, "linking to sink");
1890 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1891 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1895 /* post a warning if we have no way to configure the volume */
1897 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1898 (_("No volume control found")), ("Volume/mute is not available"));
1901 /* and ghost the sinkpad of the headmost element */
1902 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1903 pad = gst_element_get_static_pad (head, "sink");
1904 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1905 gst_object_unref (pad);
1906 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1913 if (!elem && !playsink->audio_sink) {
1914 post_missing_element_message (playsink, "autoaudiosink");
1915 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1916 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1917 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1918 (_("Both autoaudiosink and %s elements are missing."),
1919 DEFAULT_AUDIOSINK), (NULL));
1921 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1922 (_("The autoaudiosink element is missing.")), (NULL));
1925 if (playsink->audio_sink) {
1926 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1927 (_("Configured audiosink %s is not working."),
1928 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1929 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1930 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1931 (_("Both autoaudiosink and %s elements are not working."),
1932 DEFAULT_AUDIOSINK), (NULL));
1934 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1935 (_("The autoaudiosink element is not working.")), (NULL));
1938 free_chain ((GstPlayChain *) chain);
1943 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1944 (NULL), ("Failed to configure the audio sink."));
1945 /* checking sink made it READY */
1946 gst_element_set_state (chain->sink, GST_STATE_NULL);
1947 /* Remove chain from the bin to allow reuse later */
1948 gst_bin_remove (bin, chain->sink);
1949 free_chain ((GstPlayChain *) chain);
1955 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1958 GstPlayAudioChain *chain;
1959 GstStateChangeReturn ret;
1961 chain = playsink->audiochain;
1963 chain->chain.raw = raw;
1965 /* if the chain was active we don't do anything */
1966 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1969 /* try to set the sink element to READY again */
1970 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1971 if (ret == GST_STATE_CHANGE_FAILURE)
1974 /* find ts-offset element */
1975 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1976 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1979 /* check if the sink, or something within the sink, has the volume property.
1980 * If it does we don't need to add a volume element. */
1982 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1985 chain->volume = elem;
1987 if (playsink->volume_changed) {
1988 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1990 /* use the sink to control the volume */
1991 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1992 playsink->volume_changed = FALSE;
1995 g_signal_connect (chain->volume, "notify::volume",
1996 G_CALLBACK (notify_volume_cb), playsink);
1997 /* if the sink also has a mute property we can use this as well. We'll only
1998 * use the mute property if there is a volume property. We can simulate the
1999 * mute with the volume otherwise. */
2001 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2004 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2005 g_signal_connect (chain->mute, "notify::mute",
2006 G_CALLBACK (notify_mute_cb), playsink);
2009 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2011 GstPlaySinkAudioConvert *conv =
2012 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2014 /* no volume, we need to add a volume element when we can */
2015 g_object_set (chain->conv, "use-volume", TRUE, NULL);
2016 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2018 /* Disconnect signals */
2019 disconnect_chain (chain, playsink);
2022 chain->volume = conv->volume;
2023 chain->mute = chain->volume;
2025 g_signal_connect (chain->volume, "notify::volume",
2026 G_CALLBACK (notify_volume_cb), playsink);
2028 g_signal_connect (chain->mute, "notify::mute",
2029 G_CALLBACK (notify_mute_cb), playsink);
2031 /* configure with the latest volume and mute */
2032 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2033 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2036 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2042 * +-------------------------------------------------------------------+
2044 * | +----------+ +------------+ +----------+ +-------+ |
2045 * | | visqueue | | audioconv | | audiores | | vis | |
2046 * | +-sink src-sink + samp src-sink src-sink src-+ |
2047 * | | +----------+ +------------+ +----------+ +-------+ | |
2049 * +-------------------------------------------------------------------+
2052 static GstPlayVisChain *
2053 gen_vis_chain (GstPlaySink * playsink)
2055 GstPlayVisChain *chain;
2061 chain = g_new0 (GstPlayVisChain, 1);
2062 chain->chain.playsink = playsink;
2064 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2066 chain->chain.bin = gst_bin_new ("visbin");
2067 bin = GST_BIN_CAST (chain->chain.bin);
2068 gst_object_ref_sink (bin);
2070 /* we're queuing raw audio here, we can remove this queue when we can disable
2071 * async behaviour in the video sink. */
2072 chain->queue = gst_element_factory_make ("queue", "visqueue");
2073 if (chain->queue == NULL)
2075 g_object_set (chain->queue, "silent", TRUE, NULL);
2076 gst_bin_add (bin, chain->queue);
2078 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2079 if (chain->conv == NULL)
2080 goto no_audioconvert;
2081 gst_bin_add (bin, chain->conv);
2083 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2084 if (chain->resample == NULL)
2085 goto no_audioresample;
2086 gst_bin_add (bin, chain->resample);
2088 /* this pad will be used for blocking the dataflow and switching the vis
2090 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2092 if (playsink->visualisation) {
2093 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2094 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2096 if (chain->vis == NULL) {
2097 GST_DEBUG_OBJECT (playsink, "trying goom");
2098 elem = gst_element_factory_make ("goom", "vis");
2099 chain->vis = try_element (playsink, elem, TRUE);
2101 if (chain->vis == NULL)
2104 gst_bin_add (bin, chain->vis);
2106 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2107 GST_PAD_LINK_CHECK_NOTHING);
2109 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2110 GST_PAD_LINK_CHECK_NOTHING);
2112 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2113 GST_PAD_LINK_CHECK_NOTHING);
2117 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2118 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2120 pad = gst_element_get_static_pad (chain->queue, "sink");
2121 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2122 gst_object_unref (pad);
2123 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2125 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2126 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2133 post_missing_element_message (playsink, "queue");
2134 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2135 (_("Missing element '%s' - check your GStreamer installation."),
2137 free_chain ((GstPlayChain *) chain);
2142 post_missing_element_message (playsink, "audioconvert");
2143 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2144 (_("Missing element '%s' - check your GStreamer installation."),
2145 "audioconvert"), ("possibly a liboil version mismatch?"));
2146 free_chain ((GstPlayChain *) chain);
2151 post_missing_element_message (playsink, "audioresample");
2152 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2153 (_("Missing element '%s' - check your GStreamer installation."),
2154 "audioresample"), (NULL));
2155 free_chain ((GstPlayChain *) chain);
2160 post_missing_element_message (playsink, "goom");
2161 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2162 (_("Missing element '%s' - check your GStreamer installation."),
2164 free_chain ((GstPlayChain *) chain);
2169 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2170 (NULL), ("Failed to configure the visualisation element."));
2171 /* element made it to READY */
2172 gst_element_set_state (chain->vis, GST_STATE_NULL);
2173 free_chain ((GstPlayChain *) chain);
2178 /* this function is called when all the request pads are requested and when we
2179 * have to construct the final pipeline. Based on the flags we construct the
2180 * final output pipelines.
2183 gst_play_sink_reconfigure (GstPlaySink * playsink)
2186 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2188 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2190 /* assume we need nothing */
2191 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2193 GST_PLAY_SINK_LOCK (playsink);
2194 GST_OBJECT_LOCK (playsink);
2195 /* get flags, there are protected with the object lock */
2196 flags = playsink->flags;
2197 GST_OBJECT_UNLOCK (playsink);
2199 /* figure out which components we need */
2200 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2201 /* we have subtitles and we are requested to show it */
2205 if (((flags & GST_PLAY_FLAG_VIDEO)
2206 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2207 /* we have video and we are requested to show it */
2210 /* we only deinterlace if native video is not requested and
2211 * we have raw video */
2212 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2213 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2214 need_deinterlace = TRUE;
2217 if (playsink->audio_pad) {
2218 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2221 if (playsink->audio_pad_raw) {
2222 /* only can do vis with raw uncompressed audio */
2223 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2224 /* also add video when we add visualisation */
2231 /* we have a text_pad and we need text rendering, in this case we need a
2232 * video_pad to combine the video with the text or visualizations */
2233 if (need_text && !need_video) {
2234 if (playsink->video_pad) {
2236 } else if (need_audio) {
2237 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2238 (_("Can't play a text file without video or visualizations.")),
2239 ("Have text pad but no video pad or visualizations"));
2242 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2243 (_("Can't play a text file without video or visualizations.")),
2244 ("Have text pad but no video pad or visualizations"));
2245 GST_PLAY_SINK_UNLOCK (playsink);
2250 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2251 need_video, need_vis, need_text);
2253 /* set up video pipeline */
2255 gboolean raw, async;
2257 /* we need a raw sink when we do vis or when we have a raw pad */
2258 raw = need_vis ? TRUE : playsink->video_pad_raw;
2259 /* we try to set the sink async=FALSE when we need vis, this way we can
2260 * avoid a queue in the audio chain. */
2263 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2264 playsink->video_pad_raw);
2266 if (playsink->videochain) {
2267 /* try to reactivate the chain */
2268 if (!setup_video_chain (playsink, raw, async)) {
2269 if (playsink->video_sinkpad_stream_synchronizer) {
2270 gst_element_release_request_pad (GST_ELEMENT_CAST
2271 (playsink->stream_synchronizer),
2272 playsink->video_sinkpad_stream_synchronizer);
2273 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2274 playsink->video_sinkpad_stream_synchronizer = NULL;
2275 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2276 playsink->video_srcpad_stream_synchronizer = NULL;
2279 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2281 /* Remove the sink from the bin to keep its state
2282 * and unparent it to allow reuse */
2283 if (playsink->videochain->sink)
2284 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2285 playsink->videochain->sink);
2287 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2288 free_chain ((GstPlayChain *) playsink->videochain);
2289 playsink->videochain = NULL;
2293 if (!playsink->videochain)
2294 playsink->videochain = gen_video_chain (playsink, raw, async);
2295 if (!playsink->videochain)
2298 if (!playsink->video_sinkpad_stream_synchronizer) {
2301 playsink->video_sinkpad_stream_synchronizer =
2302 gst_element_get_request_pad (GST_ELEMENT_CAST
2303 (playsink->stream_synchronizer), "sink_%d");
2304 it = gst_pad_iterate_internal_links
2305 (playsink->video_sinkpad_stream_synchronizer);
2307 gst_iterator_next (it,
2308 (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2309 g_assert (playsink->video_srcpad_stream_synchronizer);
2310 gst_iterator_free (it);
2313 if (playsink->video_pad)
2314 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2315 playsink->video_sinkpad_stream_synchronizer);
2317 if (need_deinterlace) {
2318 if (!playsink->videodeinterlacechain)
2319 playsink->videodeinterlacechain =
2320 gen_video_deinterlace_chain (playsink);
2321 if (!playsink->videodeinterlacechain)
2324 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2326 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2328 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2329 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2331 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2332 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2334 if (playsink->videodeinterlacechain) {
2335 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2336 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2341 GST_DEBUG_OBJECT (playsink, "adding video chain");
2342 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2343 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2344 /* if we are not part of vis or subtitles, set the ghostpad target */
2345 if (!need_vis && !need_text && (!playsink->textchain
2346 || !playsink->text_pad)) {
2347 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2348 if (need_deinterlace)
2349 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2350 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2352 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2353 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2356 GST_DEBUG_OBJECT (playsink, "no video needed");
2357 if (playsink->videochain) {
2358 GST_DEBUG_OBJECT (playsink, "removing video chain");
2359 if (playsink->vischain) {
2362 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2364 /* also had visualisation, release the tee srcpad before we then
2365 * unlink the video from it */
2366 if (playsink->audio_tee_vissrc) {
2367 gst_element_release_request_pad (playsink->audio_tee,
2368 playsink->audio_tee_vissrc);
2369 gst_object_unref (playsink->audio_tee_vissrc);
2370 playsink->audio_tee_vissrc = NULL;
2373 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2374 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2377 if (playsink->video_sinkpad_stream_synchronizer) {
2378 gst_element_release_request_pad (GST_ELEMENT_CAST
2379 (playsink->stream_synchronizer),
2380 playsink->video_sinkpad_stream_synchronizer);
2381 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2382 playsink->video_sinkpad_stream_synchronizer = NULL;
2383 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2384 playsink->video_srcpad_stream_synchronizer = NULL;
2387 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2388 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2389 if (playsink->videochain->ts_offset)
2390 gst_object_unref (playsink->videochain->ts_offset);
2391 playsink->videochain->ts_offset = NULL;
2394 if (playsink->videodeinterlacechain) {
2395 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2396 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2399 if (playsink->video_pad)
2400 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2406 GST_DEBUG_OBJECT (playsink, "adding audio");
2408 /* get a raw sink if we are asked for a raw pad */
2409 raw = playsink->audio_pad_raw;
2411 if (playsink->audiochain) {
2412 /* try to reactivate the chain */
2413 if (!setup_audio_chain (playsink, raw)) {
2414 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2415 if (playsink->audio_tee_asrc) {
2416 gst_element_release_request_pad (playsink->audio_tee,
2417 playsink->audio_tee_asrc);
2418 gst_object_unref (playsink->audio_tee_asrc);
2419 playsink->audio_tee_asrc = NULL;
2422 if (playsink->audio_sinkpad_stream_synchronizer) {
2423 gst_element_release_request_pad (GST_ELEMENT_CAST
2424 (playsink->stream_synchronizer),
2425 playsink->audio_sinkpad_stream_synchronizer);
2426 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2427 playsink->audio_sinkpad_stream_synchronizer = NULL;
2428 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2429 playsink->audio_srcpad_stream_synchronizer = NULL;
2432 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2434 /* Remove the sink from the bin to keep its state
2435 * and unparent it to allow reuse */
2436 if (playsink->audiochain->sink)
2437 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2438 playsink->audiochain->sink);
2440 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2441 disconnect_chain (playsink->audiochain, playsink);
2442 playsink->audiochain->volume = NULL;
2443 playsink->audiochain->mute = NULL;
2444 if (playsink->audiochain->ts_offset)
2445 gst_object_unref (playsink->audiochain->ts_offset);
2446 playsink->audiochain->ts_offset = NULL;
2447 free_chain ((GstPlayChain *) playsink->audiochain);
2448 playsink->audiochain = NULL;
2449 playsink->volume_changed = playsink->mute_changed = FALSE;
2453 if (!playsink->audiochain) {
2454 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2455 playsink->audiochain = gen_audio_chain (playsink, raw);
2458 if (!playsink->audio_sinkpad_stream_synchronizer) {
2461 playsink->audio_sinkpad_stream_synchronizer =
2462 gst_element_get_request_pad (GST_ELEMENT_CAST
2463 (playsink->stream_synchronizer), "sink_%d");
2464 it = gst_pad_iterate_internal_links
2465 (playsink->audio_sinkpad_stream_synchronizer);
2467 gst_iterator_next (it,
2468 (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2469 g_assert (playsink->audio_srcpad_stream_synchronizer);
2470 gst_iterator_free (it);
2473 if (playsink->audiochain) {
2474 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2475 if (playsink->audio_tee_asrc == NULL) {
2476 playsink->audio_tee_asrc =
2477 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2479 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2480 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2481 gst_pad_link_full (playsink->audio_tee_asrc,
2482 playsink->audio_sinkpad_stream_synchronizer,
2483 GST_PAD_LINK_CHECK_NOTHING);
2484 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2485 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2488 GST_DEBUG_OBJECT (playsink, "no audio needed");
2489 /* we have no audio or we are requested to not play audio */
2490 if (playsink->audiochain) {
2491 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2492 /* release the audio pad */
2493 if (playsink->audio_tee_asrc) {
2494 gst_element_release_request_pad (playsink->audio_tee,
2495 playsink->audio_tee_asrc);
2496 gst_object_unref (playsink->audio_tee_asrc);
2497 playsink->audio_tee_asrc = NULL;
2500 if (playsink->audio_sinkpad_stream_synchronizer) {
2501 gst_element_release_request_pad (GST_ELEMENT_CAST
2502 (playsink->stream_synchronizer),
2503 playsink->audio_sinkpad_stream_synchronizer);
2504 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2505 playsink->audio_sinkpad_stream_synchronizer = NULL;
2506 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2507 playsink->audio_srcpad_stream_synchronizer = NULL;
2510 if (playsink->audiochain->sink_volume) {
2511 disconnect_chain (playsink->audiochain, playsink);
2512 playsink->audiochain->volume = NULL;
2513 playsink->audiochain->mute = NULL;
2514 if (playsink->audiochain->ts_offset)
2515 gst_object_unref (playsink->audiochain->ts_offset);
2516 playsink->audiochain->ts_offset = NULL;
2518 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2519 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2526 if (!playsink->vischain)
2527 playsink->vischain = gen_vis_chain (playsink);
2529 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2531 if (playsink->vischain) {
2532 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2534 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2535 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2536 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2537 if (playsink->audio_tee_vissrc == NULL) {
2538 playsink->audio_tee_vissrc =
2539 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2541 gst_pad_link_full (playsink->audio_tee_vissrc,
2542 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2543 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2544 GST_PAD_LINK_CHECK_NOTHING);
2545 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2546 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2547 gst_object_unref (srcpad);
2550 GST_DEBUG_OBJECT (playsink, "no vis needed");
2551 if (playsink->vischain) {
2552 if (playsink->audio_tee_vissrc) {
2553 gst_element_release_request_pad (playsink->audio_tee,
2554 playsink->audio_tee_vissrc);
2555 gst_object_unref (playsink->audio_tee_vissrc);
2556 playsink->audio_tee_vissrc = NULL;
2558 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2559 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2560 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2565 GST_DEBUG_OBJECT (playsink, "adding text");
2566 if (!playsink->textchain) {
2567 GST_DEBUG_OBJECT (playsink, "creating text chain");
2568 playsink->textchain = gen_text_chain (playsink);
2570 if (playsink->textchain) {
2573 GST_DEBUG_OBJECT (playsink, "adding text chain");
2574 if (playsink->textchain->overlay)
2575 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2577 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2579 if (!playsink->text_sinkpad_stream_synchronizer) {
2580 playsink->text_sinkpad_stream_synchronizer =
2581 gst_element_get_request_pad (GST_ELEMENT_CAST
2582 (playsink->stream_synchronizer), "sink_%d");
2583 it = gst_pad_iterate_internal_links
2584 (playsink->text_sinkpad_stream_synchronizer);
2586 gst_iterator_next (it,
2587 (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2588 g_assert (playsink->text_srcpad_stream_synchronizer);
2589 gst_iterator_free (it);
2591 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2592 playsink->text_sinkpad_stream_synchronizer);
2593 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2594 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2601 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2602 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2603 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2604 GST_PAD_LINK_CHECK_NOTHING);
2605 gst_object_unref (srcpad);
2607 if (need_deinterlace)
2608 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2609 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2611 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2612 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2614 gst_pad_link_full (playsink->textchain->srcpad,
2615 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2617 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2620 GST_DEBUG_OBJECT (playsink, "no text needed");
2621 /* we have no subtitles/text or we are requested to not show them */
2623 if (playsink->text_sinkpad_stream_synchronizer) {
2624 gst_element_release_request_pad (GST_ELEMENT_CAST
2625 (playsink->stream_synchronizer),
2626 playsink->text_sinkpad_stream_synchronizer);
2627 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2628 playsink->text_sinkpad_stream_synchronizer = NULL;
2629 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2630 playsink->text_srcpad_stream_synchronizer = NULL;
2633 if (playsink->textchain) {
2634 if (playsink->text_pad == NULL) {
2635 /* no text pad, remove the chain entirely */
2636 GST_DEBUG_OBJECT (playsink, "removing text chain");
2637 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2638 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2640 /* we have a chain and a textpad, turn the subtitles off */
2641 GST_DEBUG_OBJECT (playsink, "turning off the text");
2642 if (playsink->textchain->overlay)
2643 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2647 if (!need_video && playsink->video_pad) {
2648 if (playsink->video_sinkpad_stream_synchronizer) {
2649 gst_element_release_request_pad (GST_ELEMENT_CAST
2650 (playsink->stream_synchronizer),
2651 playsink->video_sinkpad_stream_synchronizer);
2652 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2653 playsink->video_sinkpad_stream_synchronizer = NULL;
2654 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2655 playsink->video_srcpad_stream_synchronizer = NULL;
2658 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2661 if (playsink->text_pad && !playsink->textchain)
2662 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2664 update_av_offset (playsink);
2665 do_async_done (playsink);
2666 GST_PLAY_SINK_UNLOCK (playsink);
2673 /* gen_ chain already posted error */
2674 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2675 GST_PLAY_SINK_UNLOCK (playsink);
2681 * gst_play_sink_set_flags:
2682 * @playsink: a #GstPlaySink
2683 * @flags: #GstPlayFlags
2685 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2686 * when constructing the sink pipelins.
2688 * Returns: TRUE if the flags could be configured.
2691 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2693 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2695 GST_OBJECT_LOCK (playsink);
2696 playsink->flags = flags;
2697 GST_OBJECT_UNLOCK (playsink);
2703 * gst_play_sink_get_flags:
2704 * @playsink: a #GstPlaySink
2706 * Get the flags of @playsink. That flags control the behaviour of the sink when
2707 * it constructs the sink pipelines.
2709 * Returns: the currently configured #GstPlayFlags.
2712 gst_play_sink_get_flags (GstPlaySink * playsink)
2716 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2718 GST_OBJECT_LOCK (playsink);
2719 res = playsink->flags;
2720 GST_OBJECT_UNLOCK (playsink);
2726 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2728 GstPlayTextChain *chain;
2730 GST_PLAY_SINK_LOCK (playsink);
2731 chain = (GstPlayTextChain *) playsink->textchain;
2732 g_free (playsink->font_desc);
2733 playsink->font_desc = g_strdup (desc);
2734 if (chain && chain->overlay) {
2735 g_object_set (chain->overlay, "font-desc", desc, NULL);
2737 GST_PLAY_SINK_UNLOCK (playsink);
2741 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2743 gchar *result = NULL;
2744 GstPlayTextChain *chain;
2746 GST_PLAY_SINK_LOCK (playsink);
2747 chain = (GstPlayTextChain *) playsink->textchain;
2748 if (chain && chain->overlay) {
2749 g_object_get (chain->overlay, "font-desc", &result, NULL);
2750 playsink->font_desc = g_strdup (result);
2752 result = g_strdup (playsink->font_desc);
2754 GST_PLAY_SINK_UNLOCK (playsink);
2760 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2761 const gchar * encoding)
2763 GstPlayTextChain *chain;
2765 GST_PLAY_SINK_LOCK (playsink);
2766 chain = (GstPlayTextChain *) playsink->textchain;
2767 g_free (playsink->subtitle_encoding);
2768 playsink->subtitle_encoding = g_strdup (encoding);
2769 if (chain && chain->overlay) {
2770 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2772 GST_PLAY_SINK_UNLOCK (playsink);
2776 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2778 gchar *result = NULL;
2779 GstPlayTextChain *chain;
2781 GST_PLAY_SINK_LOCK (playsink);
2782 chain = (GstPlayTextChain *) playsink->textchain;
2783 if (chain && chain->overlay) {
2784 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2785 playsink->subtitle_encoding = g_strdup (result);
2787 result = g_strdup (playsink->subtitle_encoding);
2789 GST_PLAY_SINK_UNLOCK (playsink);
2795 update_av_offset (GstPlaySink * playsink)
2798 GstPlayAudioChain *achain;
2799 GstPlayVideoChain *vchain;
2801 av_offset = playsink->av_offset;
2802 achain = (GstPlayAudioChain *) playsink->audiochain;
2803 vchain = (GstPlayVideoChain *) playsink->videochain;
2805 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2806 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2807 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2809 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2814 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2816 GST_PLAY_SINK_LOCK (playsink);
2817 playsink->av_offset = av_offset;
2818 update_av_offset (playsink);
2819 GST_PLAY_SINK_UNLOCK (playsink);
2823 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2827 GST_PLAY_SINK_LOCK (playsink);
2828 result = playsink->av_offset;
2829 GST_PLAY_SINK_UNLOCK (playsink);
2835 * gst_play_sink_get_last_frame:
2836 * @playsink: a #GstPlaySink
2838 * Get the last displayed frame from @playsink. This frame is in the native
2839 * format of the sink element, the caps on the result buffer contain the format
2840 * of the frame data.
2842 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2846 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2848 GstBuffer *result = NULL;
2849 GstPlayVideoChain *chain;
2851 GST_PLAY_SINK_LOCK (playsink);
2852 GST_DEBUG_OBJECT (playsink, "taking last frame");
2853 /* get the video chain if we can */
2854 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2855 GST_DEBUG_OBJECT (playsink, "found video chain");
2856 /* see if the chain is active */
2857 if (chain->chain.activated && chain->sink) {
2860 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2862 /* find and get the last-buffer property now */
2864 gst_play_sink_find_property (playsink, chain->sink,
2865 "last-buffer", GST_TYPE_BUFFER))) {
2866 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2867 g_object_get (elem, "last-buffer", &result, NULL);
2868 gst_object_unref (elem);
2872 GST_PLAY_SINK_UNLOCK (playsink);
2878 * gst_play_sink_convert_frame:
2879 * @playsink: a #GstPlaySink
2882 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2883 * be in the native format of the sink element and the caps on the buffer
2884 * describe the format of the frame. If @caps is not %NULL, the video
2885 * frame will be converted to the format of the caps.
2887 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2888 * available or when the conversion failed.
2891 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2895 result = gst_play_sink_get_last_frame (playsink);
2896 if (result != NULL && caps != NULL) {
2900 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2901 gst_buffer_unref (result);
2902 if (temp == NULL && err) {
2903 /* I'm really uncertain whether we should make playsink post an error
2904 * on the bus or not. It's not like it's a critical issue regarding
2905 * playsink behaviour. */
2906 GST_ERROR ("Error converting frame: %s", err->message);
2915 is_raw_structure (GstStructure * s)
2919 name = gst_structure_get_name (s);
2921 if (g_str_has_prefix (name, "video/x-raw-") ||
2922 g_str_has_prefix (name, "audio/x-raw-"))
2928 is_raw_pad (GstPad * pad)
2930 GstPad *peer = gst_pad_get_peer (pad);
2932 gboolean raw = TRUE;
2937 caps = gst_pad_get_negotiated_caps (peer);
2941 caps = gst_pad_get_caps_reffed (peer);
2943 n = gst_caps_get_size (caps);
2944 for (i = 0; i < n; i++) {
2945 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2949 } else if (raw != r) {
2950 GST_ERROR_OBJECT (pad,
2951 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2957 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2959 gst_caps_unref (caps);
2960 gst_object_unref (peer);
2966 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
2968 GstPlaySink *playsink = (GstPlaySink *) user_data;
2971 GST_PLAY_SINK_LOCK (playsink);
2973 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2974 if (pad == playsink->video_pad) {
2975 playsink->video_pad_blocked = blocked;
2976 GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
2978 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2979 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2981 } else if (pad == playsink->audio_pad) {
2982 playsink->audio_pad_blocked = blocked;
2983 GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
2985 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2986 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2988 } else if (pad == playsink->text_pad) {
2989 playsink->text_pad_blocked = blocked;
2990 GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
2992 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
2996 gst_object_unref (pad);
2997 GST_PLAY_SINK_UNLOCK (playsink);
3001 /* We reconfigure when for ALL streams:
3002 * * there isn't a pad
3003 * * OR the pad is blocked
3004 * * OR there are no pending blocks on that pad
3007 if ((!playsink->video_pad || playsink->video_pad_blocked
3008 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3009 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3010 && (!playsink->text_pad || playsink->text_pad_blocked
3011 || !PENDING_TEXT_BLOCK (playsink))) {
3012 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3014 if (playsink->video_pad) {
3015 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3016 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3017 playsink->video_pad_raw);
3020 if (playsink->audio_pad) {
3021 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3022 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3023 playsink->audio_pad_raw);
3026 gst_play_sink_reconfigure (playsink);
3028 if (playsink->video_pad) {
3030 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3031 (playsink->video_pad)));
3032 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3033 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3034 gst_object_unref (opad);
3037 if (playsink->audio_pad) {
3039 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3040 (playsink->audio_pad)));
3041 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3042 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3043 gst_object_unref (opad);
3046 if (playsink->text_pad) {
3048 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3049 (playsink->text_pad)));
3050 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3051 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3052 gst_object_unref (opad);
3056 gst_object_unref (pad);
3058 GST_PLAY_SINK_UNLOCK (playsink);
3062 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3064 gboolean reconfigure = FALSE;
3068 g_object_get (pad, "caps", &caps, NULL);
3072 if (pad == playsink->audio_pad) {
3073 raw = is_raw_pad (pad);
3074 reconfigure = (!!playsink->audio_pad_raw != !!raw)
3075 && playsink->audiochain;
3076 GST_DEBUG_OBJECT (pad,
3077 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3079 } else if (pad == playsink->video_pad) {
3080 raw = is_raw_pad (pad);
3081 reconfigure = (!!playsink->video_pad_raw != !!raw)
3082 && playsink->videochain;
3083 GST_DEBUG_OBJECT (pad,
3084 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3088 gst_caps_unref (caps);
3091 GST_PLAY_SINK_LOCK (playsink);
3092 if (playsink->video_pad) {
3094 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3095 (playsink->video_pad)));
3096 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3097 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3098 gst_object_unref (opad);
3101 if (playsink->audio_pad) {
3103 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3104 (playsink->audio_pad)));
3105 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3106 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3107 gst_object_unref (opad);
3110 if (playsink->text_pad) {
3112 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3113 (playsink->text_pad)));
3114 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3115 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3116 gst_object_unref (opad);
3118 GST_PLAY_SINK_UNLOCK (playsink);
3123 * gst_play_sink_request_pad
3124 * @playsink: a #GstPlaySink
3125 * @type: a #GstPlaySinkType
3127 * Create or return a pad of @type.
3129 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3132 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3135 gboolean created = FALSE;
3136 gboolean activate = TRUE;
3137 const gchar *pad_name = NULL;
3139 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3141 GST_PLAY_SINK_LOCK (playsink);
3143 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3144 case GST_PLAY_SINK_TYPE_AUDIO:
3145 pad_name = "audio_sink";
3146 if (!playsink->audio_tee) {
3147 GST_LOG_OBJECT (playsink, "creating tee");
3148 /* create tee when needed. This element will feed the audio sink chain
3149 * and the vis chain. */
3150 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3151 if (playsink->audio_tee == NULL) {
3152 post_missing_element_message (playsink, "tee");
3153 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3154 (_("Missing element '%s' - check your GStreamer installation."),
3159 playsink->audio_tee_sink =
3160 gst_element_get_static_pad (playsink->audio_tee, "sink");
3161 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3162 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3165 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3167 if (!playsink->audio_pad) {
3168 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3169 playsink->audio_pad =
3170 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3171 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3172 G_CALLBACK (caps_notify_cb), playsink);
3175 playsink->audio_pad_raw = FALSE;
3176 res = playsink->audio_pad;
3178 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3179 case GST_PLAY_SINK_TYPE_VIDEO:
3180 pad_name = "video_sink";
3181 if (!playsink->video_pad) {
3182 GST_LOG_OBJECT (playsink, "ghosting videosink");
3183 playsink->video_pad =
3184 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3185 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3186 G_CALLBACK (caps_notify_cb), playsink);
3189 playsink->video_pad_raw = FALSE;
3190 res = playsink->video_pad;
3192 case GST_PLAY_SINK_TYPE_TEXT:
3193 GST_LOG_OBJECT (playsink, "ghosting text");
3194 if (!playsink->text_pad) {
3195 playsink->text_pad =
3196 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3199 res = playsink->text_pad;
3201 case GST_PLAY_SINK_TYPE_FLUSHING:
3205 /* we need a unique padname for the flushing pad. */
3206 padname = g_strdup_printf ("flushing_%d", playsink->count);
3207 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3218 GST_PLAY_SINK_UNLOCK (playsink);
3220 if (created && res) {
3221 /* we have to add the pad when it's active or we get an error when the
3222 * element is 'running' */
3223 gst_pad_set_active (res, TRUE);
3224 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3225 if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3227 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3229 gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3230 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3231 PENDING_FLAG_SET (playsink, type);
3232 gst_object_unref (blockpad);
3235 gst_pad_set_active (res, activate);
3242 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3247 GstPlaySinkType type;
3248 const gchar *tplname;
3250 g_return_val_if_fail (templ != NULL, NULL);
3252 GST_DEBUG_OBJECT (element, "name:%s", name);
3254 psink = GST_PLAY_SINK (element);
3255 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3257 /* Figure out the GstPlaySinkType based on the template */
3258 if (!strcmp (tplname, "audio_sink"))
3259 type = GST_PLAY_SINK_TYPE_AUDIO;
3260 else if (!strcmp (tplname, "audio_raw_sink"))
3261 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3262 else if (!strcmp (tplname, "video_sink"))
3263 type = GST_PLAY_SINK_TYPE_VIDEO;
3264 else if (!strcmp (tplname, "video_raw_sink"))
3265 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3266 else if (!strcmp (tplname, "text_sink"))
3267 type = GST_PLAY_SINK_TYPE_TEXT;
3269 goto unknown_template;
3271 pad = gst_play_sink_request_pad (psink, type);
3275 GST_WARNING_OBJECT (element, "Unknown pad template");
3280 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3282 GstPad **res = NULL;
3283 gboolean untarget = TRUE;
3285 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3287 GST_PLAY_SINK_LOCK (playsink);
3288 if (pad == playsink->video_pad) {
3289 res = &playsink->video_pad;
3290 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3292 } else if (pad == playsink->audio_pad) {
3293 res = &playsink->audio_pad;
3294 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3296 } else if (pad == playsink->text_pad) {
3297 res = &playsink->text_pad;
3299 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3303 GST_PLAY_SINK_UNLOCK (playsink);
3306 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3307 gst_pad_set_active (*res, FALSE);
3309 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3310 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3312 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3313 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3319 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3321 GstPlaySink *psink = GST_PLAY_SINK (element);
3323 gst_play_sink_release_pad (psink, pad);
3327 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3329 GstPlaySink *playsink;
3331 playsink = GST_PLAY_SINK_CAST (bin);
3333 switch (GST_MESSAGE_TYPE (message)) {
3334 case GST_MESSAGE_STEP_DONE:
3339 gboolean flush, intermediate, eos;
3342 GST_INFO_OBJECT (playsink, "Handling step-done message");
3343 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3344 &intermediate, &duration, &eos);
3346 if (format == GST_FORMAT_BUFFERS) {
3347 /* for the buffer format, we align the other streams */
3348 if (playsink->audiochain) {
3352 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3355 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3356 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3360 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3364 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3369 /* Send an event to our sinks until one of them works; don't then send to the
3370 * remaining sinks (unlike GstBin)
3371 * Special case: If a text sink is set we need to send the event
3372 * to them in case it's source is different from the a/v stream's source.
3375 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3377 gboolean res = TRUE;
3379 if (playsink->textchain && playsink->textchain->sink) {
3380 gst_event_ref (event);
3381 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3382 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3384 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3388 if (playsink->videochain) {
3389 gst_event_ref (event);
3390 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3391 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3394 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3396 if (playsink->audiochain) {
3397 gst_event_ref (event);
3398 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3399 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3402 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3406 gst_event_unref (event);
3410 /* We only want to send the event to a single sink (overriding GstBin's
3411 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3412 * events appropriately. So, this is a messy duplication of code. */
3414 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3416 gboolean res = FALSE;
3417 GstEventType event_type = GST_EVENT_TYPE (event);
3418 GstPlaySink *playsink;
3420 playsink = GST_PLAY_SINK_CAST (element);
3422 switch (event_type) {
3423 case GST_EVENT_SEEK:
3424 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3425 res = gst_play_sink_send_event_to_sink (playsink, event);
3427 case GST_EVENT_STEP:
3432 gboolean flush, intermediate;
3434 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3437 if (format == GST_FORMAT_BUFFERS) {
3438 /* for buffers, we will try to step video frames, for other formats we
3439 * send the step to all sinks */
3440 res = gst_play_sink_send_event_to_sink (playsink, event);
3443 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3450 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3457 static GstStateChangeReturn
3458 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3460 GstStateChangeReturn ret;
3461 GstStateChangeReturn bret;
3463 GstPlaySink *playsink;
3465 playsink = GST_PLAY_SINK (element);
3467 switch (transition) {
3468 case GST_STATE_CHANGE_READY_TO_PAUSED:
3469 playsink->need_async_start = TRUE;
3470 /* we want to go async to PAUSED until we managed to configure and add the
3472 do_async_start (playsink);
3473 ret = GST_STATE_CHANGE_ASYNC;
3475 case GST_STATE_CHANGE_PAUSED_TO_READY:
3476 /* unblock all pads here */
3477 GST_PLAY_SINK_LOCK (playsink);
3478 if (playsink->video_pad) {
3480 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3481 (playsink->video_pad)));
3482 if (gst_pad_is_blocked (opad)) {
3483 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3484 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3486 gst_object_unref (opad);
3487 playsink->video_pad_blocked = FALSE;
3490 if (playsink->audio_pad) {
3492 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3493 (playsink->audio_pad)));
3495 if (gst_pad_is_blocked (opad)) {
3496 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3497 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3499 gst_object_unref (opad);
3500 playsink->audio_pad_blocked = FALSE;
3503 if (playsink->text_pad) {
3505 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3506 (playsink->text_pad)));
3507 if (gst_pad_is_blocked (opad)) {
3508 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3509 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3511 gst_object_unref (opad);
3512 playsink->text_pad_blocked = FALSE;
3514 GST_PLAY_SINK_UNLOCK (playsink);
3516 case GST_STATE_CHANGE_READY_TO_NULL:
3517 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3518 /* remove our links to the mute and volume elements when they were
3519 * provided by a sink */
3520 disconnect_chain (playsink->audiochain, playsink);
3521 playsink->audiochain->volume = NULL;
3522 playsink->audiochain->mute = NULL;
3525 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3526 gst_object_unref (playsink->audiochain->ts_offset);
3527 playsink->audiochain->ts_offset = NULL;
3530 if (playsink->videochain && playsink->videochain->ts_offset) {
3531 gst_object_unref (playsink->videochain->ts_offset);
3532 playsink->videochain->ts_offset = NULL;
3534 ret = GST_STATE_CHANGE_SUCCESS;
3537 /* all other state changes return SUCCESS by default, this value can be
3538 * overridden by the result of the children */
3539 ret = GST_STATE_CHANGE_SUCCESS;
3543 /* do the state change of the children */
3545 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3547 /* now look at the result of our children and adjust the return value */
3549 case GST_STATE_CHANGE_FAILURE:
3550 /* failure, we stop */
3551 goto activate_failed;
3552 case GST_STATE_CHANGE_NO_PREROLL:
3553 /* some child returned NO_PREROLL. This is strange but we never know. We
3554 * commit our async state change (if any) and return the NO_PREROLL */
3555 do_async_done (playsink);
3558 case GST_STATE_CHANGE_ASYNC:
3559 /* some child was async, return this */
3563 /* return our previously configured return value */
3567 switch (transition) {
3568 case GST_STATE_CHANGE_READY_TO_PAUSED:
3570 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3571 /* FIXME Release audio device when we implement that */
3572 playsink->need_async_start = TRUE;
3574 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3575 if (playsink->video_sinkpad_stream_synchronizer) {
3576 gst_element_release_request_pad (GST_ELEMENT_CAST
3577 (playsink->stream_synchronizer),
3578 playsink->video_sinkpad_stream_synchronizer);
3579 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3580 playsink->video_sinkpad_stream_synchronizer = NULL;
3581 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3582 playsink->video_srcpad_stream_synchronizer = NULL;
3584 if (playsink->audio_sinkpad_stream_synchronizer) {
3585 gst_element_release_request_pad (GST_ELEMENT_CAST
3586 (playsink->stream_synchronizer),
3587 playsink->audio_sinkpad_stream_synchronizer);
3588 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3589 playsink->audio_sinkpad_stream_synchronizer = NULL;
3590 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3591 playsink->audio_srcpad_stream_synchronizer = NULL;
3593 if (playsink->text_sinkpad_stream_synchronizer) {
3594 gst_element_release_request_pad (GST_ELEMENT_CAST
3595 (playsink->stream_synchronizer),
3596 playsink->text_sinkpad_stream_synchronizer);
3597 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3598 playsink->text_sinkpad_stream_synchronizer = NULL;
3599 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3600 playsink->text_srcpad_stream_synchronizer = NULL;
3604 case GST_STATE_CHANGE_READY_TO_NULL:
3605 /* remove sinks we added */
3606 if (playsink->videodeinterlacechain) {
3607 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3609 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3611 if (playsink->videochain) {
3612 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3613 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3615 if (playsink->audiochain) {
3616 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3617 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3619 if (playsink->vischain) {
3620 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3621 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3623 if (playsink->textchain) {
3624 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3625 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3627 do_async_done (playsink);
3628 /* when going to READY, keep elements around as long as possible,
3629 * so they may be re-used faster next time/url around.
3630 * when really going to NULL, clean up everything completely. */
3631 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3633 /* Unparent the sinks to allow reuse */
3634 if (playsink->videochain && playsink->videochain->sink)
3635 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3636 playsink->videochain->sink);
3637 if (playsink->audiochain && playsink->audiochain->sink)
3638 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3639 playsink->audiochain->sink);
3640 if (playsink->textchain && playsink->textchain->sink)
3641 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3642 playsink->textchain->sink);
3644 if (playsink->audio_sink != NULL)
3645 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3646 if (playsink->video_sink != NULL)
3647 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3648 if (playsink->visualisation != NULL)
3649 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3650 if (playsink->text_sink != NULL)
3651 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3653 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3654 playsink->videodeinterlacechain = NULL;
3655 free_chain ((GstPlayChain *) playsink->videochain);
3656 playsink->videochain = NULL;
3657 free_chain ((GstPlayChain *) playsink->audiochain);
3658 playsink->audiochain = NULL;
3659 free_chain ((GstPlayChain *) playsink->vischain);
3660 playsink->vischain = NULL;
3661 free_chain ((GstPlayChain *) playsink->textchain);
3662 playsink->textchain = NULL;
3673 GST_DEBUG_OBJECT (element,
3674 "element failed to change states -- activation problem?");
3675 return GST_STATE_CHANGE_FAILURE;
3680 gst_play_sink_set_property (GObject * object, guint prop_id,
3681 const GValue * value, GParamSpec * spec)
3683 GstPlaySink *playsink = GST_PLAY_SINK (object);
3687 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3690 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3693 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3695 case PROP_FONT_DESC:
3696 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3698 case PROP_SUBTITLE_ENCODING:
3699 gst_play_sink_set_subtitle_encoding (playsink,
3700 g_value_get_string (value));
3702 case PROP_VIS_PLUGIN:
3703 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3705 case PROP_AV_OFFSET:
3706 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3708 case PROP_VIDEO_SINK:
3709 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3710 g_value_get_object (value));
3712 case PROP_AUDIO_SINK:
3713 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3714 g_value_get_object (value));
3716 case PROP_TEXT_SINK:
3717 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3718 g_value_get_object (value));
3721 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3727 gst_play_sink_get_property (GObject * object, guint prop_id,
3728 GValue * value, GParamSpec * spec)
3730 GstPlaySink *playsink = GST_PLAY_SINK (object);
3734 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3737 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3740 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3742 case PROP_FONT_DESC:
3743 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3745 case PROP_SUBTITLE_ENCODING:
3746 g_value_take_string (value,
3747 gst_play_sink_get_subtitle_encoding (playsink));
3749 case PROP_VIS_PLUGIN:
3750 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3753 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3755 case PROP_AV_OFFSET:
3756 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3758 case PROP_VIDEO_SINK:
3759 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3760 GST_PLAY_SINK_TYPE_VIDEO));
3762 case PROP_AUDIO_SINK:
3763 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3764 GST_PLAY_SINK_TYPE_AUDIO));
3766 case PROP_TEXT_SINK:
3767 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3768 GST_PLAY_SINK_TYPE_TEXT));
3771 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3778 gst_play_sink_plugin_init (GstPlugin * plugin)
3780 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3782 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3783 GST_TYPE_PLAY_SINK);