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;
162 GstPlayAudioChain *audiochain;
163 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
164 GstPlayVideoChain *videochain;
165 GstPlayVisChain *vischain;
166 GstPlayTextChain *textchain;
170 gboolean audio_pad_raw;
171 gboolean audio_pad_blocked;
172 GstPad *audio_srcpad_stream_synchronizer;
173 GstPad *audio_sinkpad_stream_synchronizer;
174 GstPad *audio_srcpad_policy;
175 GstPad *audio_sinkpad_policy;
177 GstElement *audio_tee;
178 GstPad *audio_tee_sink;
179 GstPad *audio_tee_asrc;
180 GstPad *audio_tee_vissrc;
183 gboolean video_pad_raw;
184 gboolean video_pad_blocked;
185 GstPad *video_srcpad_stream_synchronizer;
186 GstPad *video_sinkpad_stream_synchronizer;
187 GstPad *video_srcpad_policy;
188 GstPad *video_sinkpad_policy;
191 gboolean text_pad_blocked;
192 GstPad *text_srcpad_stream_synchronizer;
193 GstPad *text_sinkpad_stream_synchronizer;
194 GstPad *text_srcpad_policy;
195 GstPad *text_sinkpad_policy;
197 guint32 pending_blocked_pads;
200 GstElement *audio_sink;
201 GstElement *video_sink;
202 GstElement *visualisation;
203 GstElement *text_sink;
206 gchar *font_desc; /* font description */
207 gchar *subtitle_encoding; /* subtitle encoding */
208 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
210 gboolean volume_changed; /* volume/mute changed while no audiochain */
211 gboolean mute_changed; /* ... has been created yet */
215 struct _GstPlaySinkClass
217 GstBinClass parent_class;
219 gboolean (*reconfigure) (GstPlaySink * playsink);
221 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
225 static GstStaticPadTemplate audiotemplate =
226 GST_STATIC_PAD_TEMPLATE ("audio_sink",
229 GST_STATIC_CAPS_ANY);
230 static GstStaticPadTemplate videotemplate =
231 GST_STATIC_PAD_TEMPLATE ("video_sink",
234 GST_STATIC_CAPS_ANY);
235 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
238 GST_STATIC_CAPS_ANY);
240 /* FIXME 0.11: Remove */
241 static GstStaticPadTemplate audiorawtemplate =
242 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
245 GST_STATIC_CAPS_ANY);
246 static GstStaticPadTemplate videorawtemplate =
247 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
250 GST_STATIC_CAPS_ANY);
261 PROP_SUBTITLE_ENCODING,
277 static void gst_play_sink_dispose (GObject * object);
278 static void gst_play_sink_finalize (GObject * object);
279 static void gst_play_sink_set_property (GObject * object, guint prop_id,
280 const GValue * value, GParamSpec * spec);
281 static void gst_play_sink_get_property (GObject * object, guint prop_id,
282 GValue * value, GParamSpec * spec);
284 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
285 GstPadTemplate * templ, const gchar * name);
286 static void gst_play_sink_release_request_pad (GstElement * element,
288 static gboolean gst_play_sink_send_event (GstElement * element,
290 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
291 GstStateChange transition);
293 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
295 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
296 GstPlaySink * playsink);
297 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
298 GstPlaySink * playsink);
300 static void update_av_offset (GstPlaySink * playsink);
303 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
304 GValue * return_value G_GNUC_UNUSED,
305 guint n_param_values,
306 const GValue * param_values,
307 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
309 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
310 gpointer arg_1, gpointer data2);
311 register GMarshalFunc_OBJECT__BOXED callback;
312 register GCClosure *cc = (GCClosure *) closure;
313 register gpointer data1, data2;
315 g_return_if_fail (return_value != NULL);
316 g_return_if_fail (n_param_values == 2);
318 if (G_CCLOSURE_SWAP_DATA (closure)) {
319 data1 = closure->data;
320 data2 = g_value_peek_pointer (param_values + 0);
322 data1 = g_value_peek_pointer (param_values + 0);
323 data2 = closure->data;
326 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
328 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
330 gst_value_take_buffer (return_value, v_return);
333 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
335 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
338 gst_play_sink_class_init (GstPlaySinkClass * klass)
340 GObjectClass *gobject_klass;
341 GstElementClass *gstelement_klass;
342 GstBinClass *gstbin_klass;
344 gobject_klass = (GObjectClass *) klass;
345 gstelement_klass = (GstElementClass *) klass;
346 gstbin_klass = (GstBinClass *) klass;
348 gobject_klass->dispose = gst_play_sink_dispose;
349 gobject_klass->finalize = gst_play_sink_finalize;
350 gobject_klass->set_property = gst_play_sink_set_property;
351 gobject_klass->get_property = gst_play_sink_get_property;
357 * Control the behaviour of playsink.
359 g_object_class_install_property (gobject_klass, PROP_FLAGS,
360 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
361 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
362 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
365 * GstPlaySink:volume:
367 * Get or set the current audio stream volume. 1.0 means 100%,
368 * 0.0 means mute. This uses a linear volume scale.
371 g_object_class_install_property (gobject_klass, PROP_VOLUME,
372 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
373 0.0, VOLUME_MAX_DOUBLE, 1.0,
374 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
375 g_object_class_install_property (gobject_klass, PROP_MUTE,
376 g_param_spec_boolean ("mute", "Mute",
377 "Mute the audio channel without changing the volume", FALSE,
378 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
379 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
380 g_param_spec_string ("subtitle-font-desc",
381 "Subtitle font description",
382 "Pango font description of font "
383 "to be used for subtitle rendering", NULL,
384 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
385 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
386 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
387 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
388 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
389 "be checked for an encoding to use. If that is not set either, "
390 "ISO-8859-15 will be assumed.", NULL,
391 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
392 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
393 g_param_spec_object ("vis-plugin", "Vis plugin",
394 "the visualization element to use (NULL = default)",
395 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
399 * Get the currently rendered or prerolled frame in the video sink.
400 * The #GstCaps on the buffer will describe the format of the buffer.
404 g_object_class_install_property (gobject_klass, PROP_FRAME,
405 gst_param_spec_mini_object ("frame", "Frame",
406 "The last frame (NULL = no video available)",
407 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
409 * GstPlaySink:av-offset:
411 * Control the synchronisation offset between the audio and video streams.
412 * Positive values make the audio ahead of the video and negative values make
413 * the audio go behind the video.
417 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
418 g_param_spec_int64 ("av-offset", "AV Offset",
419 "The synchronisation offset between audio and video in nanoseconds",
420 G_MININT64, G_MAXINT64, 0,
421 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
424 * GstPlaySink:video-sink:
426 * Set the used video sink element. NULL will use the default sink. playsink
427 * must be in %GST_STATE_NULL
431 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
432 g_param_spec_object ("video-sink", "Video Sink",
433 "the video output element to use (NULL = default sink)",
434 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
436 * GstPlaySink:audio-sink:
438 * Set the used audio sink element. NULL will use the default sink. playsink
439 * must be in %GST_STATE_NULL
443 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
444 g_param_spec_object ("audio-sink", "Audio Sink",
445 "the audio output element to use (NULL = default sink)",
446 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
448 * GstPlaySink:text-sink:
450 * Set the used text sink element. NULL will use the default sink. playsink
451 * must be in %GST_STATE_NULL
455 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
456 g_param_spec_object ("text-sink", "Text sink",
457 "the text output element to use (NULL = default textoverlay)",
458 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
461 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
462 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
463 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
466 * GstPlaySink::convert-frame
467 * @playsink: a #GstPlaySink
468 * @caps: the target format of the frame
470 * Action signal to retrieve the currently playing video frame in the format
471 * specified by @caps.
472 * If @caps is %NULL, no conversion will be performed and this function is
473 * equivalent to the #GstPlaySink::frame property.
475 * Returns: a #GstBuffer of the current video frame converted to #caps.
476 * The caps on the buffer will describe the final layout of the buffer data.
477 * %NULL is returned when no current buffer can be retrieved or when the
482 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
483 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
484 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
485 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
487 gst_element_class_add_static_pad_template (gstelement_klass,
489 gst_element_class_add_static_pad_template (gstelement_klass,
491 gst_element_class_add_static_pad_template (gstelement_klass,
493 gst_element_class_add_static_pad_template (gstelement_klass,
495 gst_element_class_add_static_pad_template (gstelement_klass,
497 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
499 "Convenience sink for multiple streams",
500 "Wim Taymans <wim.taymans@gmail.com>");
502 gstelement_klass->change_state =
503 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
504 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
505 gstelement_klass->request_new_pad =
506 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
507 gstelement_klass->release_pad =
508 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
510 gstbin_klass->handle_message =
511 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
513 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
514 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
518 gst_play_sink_init (GstPlaySink * playsink)
521 playsink->video_sink = NULL;
522 playsink->audio_sink = NULL;
523 playsink->visualisation = NULL;
524 playsink->text_sink = NULL;
525 playsink->volume = 1.0;
526 playsink->font_desc = NULL;
527 playsink->subtitle_encoding = NULL;
528 playsink->flags = DEFAULT_FLAGS;
530 playsink->stream_synchronizer =
531 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
532 gst_bin_add (GST_BIN_CAST (playsink),
533 GST_ELEMENT_CAST (playsink->stream_synchronizer));
536 gst_element_factory_make ("autopolicy", "policy");
537 gst_bin_add (GST_BIN_CAST (playsink), playsink->policy);
539 g_static_rec_mutex_init (&playsink->lock);
540 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
544 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
548 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
551 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
557 free_chain (GstPlayChain * chain)
561 gst_object_unref (chain->bin);
567 gst_play_sink_dispose (GObject * object)
569 GstPlaySink *playsink;
571 playsink = GST_PLAY_SINK (object);
573 if (playsink->audio_sink != NULL) {
574 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
575 gst_object_unref (playsink->audio_sink);
576 playsink->audio_sink = NULL;
578 if (playsink->video_sink != NULL) {
579 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
580 gst_object_unref (playsink->video_sink);
581 playsink->video_sink = NULL;
583 if (playsink->visualisation != NULL) {
584 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
585 gst_object_unref (playsink->visualisation);
586 playsink->visualisation = NULL;
588 if (playsink->text_sink != NULL) {
589 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
590 gst_object_unref (playsink->text_sink);
591 playsink->text_sink = NULL;
594 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
595 playsink->videodeinterlacechain = NULL;
596 free_chain ((GstPlayChain *) playsink->videochain);
597 playsink->videochain = NULL;
598 free_chain ((GstPlayChain *) playsink->audiochain);
599 playsink->audiochain = NULL;
600 free_chain ((GstPlayChain *) playsink->vischain);
601 playsink->vischain = NULL;
602 free_chain ((GstPlayChain *) playsink->textchain);
603 playsink->textchain = NULL;
605 if (playsink->audio_tee_sink) {
606 gst_object_unref (playsink->audio_tee_sink);
607 playsink->audio_tee_sink = NULL;
610 if (playsink->audio_tee_vissrc) {
611 gst_element_release_request_pad (playsink->audio_tee,
612 playsink->audio_tee_vissrc);
613 gst_object_unref (playsink->audio_tee_vissrc);
614 playsink->audio_tee_vissrc = NULL;
617 if (playsink->audio_tee_asrc) {
618 gst_element_release_request_pad (playsink->audio_tee,
619 playsink->audio_tee_asrc);
620 gst_object_unref (playsink->audio_tee_asrc);
621 playsink->audio_tee_asrc = NULL;
624 g_free (playsink->font_desc);
625 playsink->font_desc = NULL;
627 g_free (playsink->subtitle_encoding);
628 playsink->subtitle_encoding = NULL;
630 playsink->stream_synchronizer = NULL;
631 playsink->policy = NULL;
633 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
637 gst_play_sink_finalize (GObject * object)
639 GstPlaySink *playsink;
641 playsink = GST_PLAY_SINK (object);
643 g_static_rec_mutex_free (&playsink->lock);
645 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
649 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
652 GstElement **elem = NULL, *old = NULL;
654 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
656 GST_PLAY_SINK_LOCK (playsink);
658 case GST_PLAY_SINK_TYPE_AUDIO:
659 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
660 elem = &playsink->audio_sink;
662 case GST_PLAY_SINK_TYPE_VIDEO:
663 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
664 elem = &playsink->video_sink;
666 case GST_PLAY_SINK_TYPE_TEXT:
667 elem = &playsink->text_sink;
675 gst_object_ref (sink);
678 GST_PLAY_SINK_UNLOCK (playsink);
682 gst_element_set_state (old, GST_STATE_NULL);
683 gst_object_unref (old);
688 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
690 GstElement *result = NULL;
691 GstElement *elem = NULL, *chainp = NULL;
693 GST_PLAY_SINK_LOCK (playsink);
695 case GST_PLAY_SINK_TYPE_AUDIO:
696 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
698 GstPlayAudioChain *chain;
699 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
700 chainp = chain->sink;
701 elem = playsink->audio_sink;
704 case GST_PLAY_SINK_TYPE_VIDEO:
705 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
707 GstPlayVideoChain *chain;
708 if ((chain = (GstPlayVideoChain *) playsink->videochain))
709 chainp = chain->sink;
710 elem = playsink->video_sink;
713 case GST_PLAY_SINK_TYPE_TEXT:
715 GstPlayTextChain *chain;
716 if ((chain = (GstPlayTextChain *) playsink->textchain))
717 chainp = chain->sink;
718 elem = playsink->text_sink;
725 /* we have an active chain with a sink, get the sink */
726 result = gst_object_ref (chainp);
728 /* nothing found, return last configured sink */
729 if (result == NULL && elem)
730 result = gst_object_ref (elem);
731 GST_PLAY_SINK_UNLOCK (playsink);
737 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
740 GstPlaySink *playsink;
742 playsink = GST_PLAY_SINK (user_data);
743 /* nothing to do here, we need a dummy callback here to make the async call
745 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
749 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
752 GstPlaySink *playsink;
753 GstPlayVisChain *chain;
755 playsink = GST_PLAY_SINK (user_data);
757 GST_PLAY_SINK_LOCK (playsink);
758 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
759 /* now try to change the plugin in the running vis chain */
760 if (!(chain = (GstPlayVisChain *) playsink->vischain))
763 /* unlink the old plugin and unghost the pad */
764 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
765 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
767 /* set the old plugin to NULL and remove */
768 gst_element_set_state (chain->vis, GST_STATE_NULL);
769 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
771 /* add new plugin and set state to playing */
772 chain->vis = playsink->visualisation;
773 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
774 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
777 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
778 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
781 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
782 GST_PAD_LINK_CHECK_NOTHING);
783 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
787 /* Unblock the pad */
788 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
790 GST_PLAY_SINK_UNLOCK (playsink);
794 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
796 GstPlayVisChain *chain;
798 /* setting NULL means creating the default vis plugin */
800 vis = gst_element_factory_make ("goom", "vis");
802 /* simply return if we don't have a vis plugin here */
806 GST_PLAY_SINK_LOCK (playsink);
807 /* first store the new vis */
808 if (playsink->visualisation)
809 gst_object_unref (playsink->visualisation);
811 gst_object_ref_sink (vis);
812 playsink->visualisation = vis;
814 /* now try to change the plugin in the running vis chain, if we have no chain,
815 * we don't bother, any future vis chain will be created with the new vis
817 if (!(chain = (GstPlayVisChain *) playsink->vischain))
820 /* block the pad, the next time the callback is called we can change the
821 * visualisation. It's possible that this never happens or that the pad was
822 * already blocked. If the callback never happens, we don't have new data so
823 * we don't need the new vis plugin. If the pad was already blocked, the
824 * function returns FALSE but the previous pad block will do the right thing
826 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
827 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
830 GST_PLAY_SINK_UNLOCK (playsink);
836 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
838 GstElement *result = NULL;
839 GstPlayVisChain *chain;
841 GST_PLAY_SINK_LOCK (playsink);
842 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
843 /* we have an active chain, get the sink */
845 result = gst_object_ref (chain->vis);
847 /* nothing found, return last configured sink */
848 if (result == NULL && playsink->visualisation)
849 result = gst_object_ref (playsink->visualisation);
850 GST_PLAY_SINK_UNLOCK (playsink);
856 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
858 GstPlayAudioChain *chain;
860 GST_PLAY_SINK_LOCK (playsink);
861 playsink->volume = volume;
862 chain = (GstPlayAudioChain *) playsink->audiochain;
863 if (chain && chain->volume) {
864 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
865 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
866 chain->mute, volume, playsink->mute);
867 /* if there is a mute element or we are not muted, set the volume */
868 if (chain->mute || !playsink->mute)
869 g_object_set (chain->volume, "volume", volume, NULL);
871 GST_LOG_OBJECT (playsink, "no volume element");
872 playsink->volume_changed = TRUE;
874 GST_PLAY_SINK_UNLOCK (playsink);
878 gst_play_sink_get_volume (GstPlaySink * playsink)
881 GstPlayAudioChain *chain;
883 GST_PLAY_SINK_LOCK (playsink);
884 chain = (GstPlayAudioChain *) playsink->audiochain;
885 result = playsink->volume;
886 if (chain && chain->volume) {
887 if (chain->mute || !playsink->mute) {
888 g_object_get (chain->volume, "volume", &result, NULL);
889 playsink->volume = result;
892 GST_PLAY_SINK_UNLOCK (playsink);
898 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
900 GstPlayAudioChain *chain;
902 GST_PLAY_SINK_LOCK (playsink);
903 playsink->mute = mute;
904 chain = (GstPlayAudioChain *) playsink->audiochain;
907 g_object_set (chain->mute, "mute", mute, NULL);
908 } else if (chain->volume) {
910 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
912 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
916 playsink->mute_changed = TRUE;
918 GST_PLAY_SINK_UNLOCK (playsink);
922 gst_play_sink_get_mute (GstPlaySink * playsink)
925 GstPlayAudioChain *chain;
927 GST_PLAY_SINK_LOCK (playsink);
928 chain = (GstPlayAudioChain *) playsink->audiochain;
929 if (chain && chain->mute) {
930 g_object_get (chain->mute, "mute", &result, NULL);
931 playsink->mute = result;
933 result = playsink->mute;
935 GST_PLAY_SINK_UNLOCK (playsink);
941 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
945 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
946 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
950 add_chain (GstPlayChain * chain, gboolean add)
952 if (chain->added == add)
956 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
958 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
959 /* we don't want to lose our sink status */
960 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
969 activate_chain (GstPlayChain * chain, gboolean activate)
973 if (chain->activated == activate)
976 GST_OBJECT_LOCK (chain->playsink);
977 state = GST_STATE_TARGET (chain->playsink);
978 GST_OBJECT_UNLOCK (chain->playsink);
981 gst_element_set_state (chain->bin, state);
983 gst_element_set_state (chain->bin, GST_STATE_NULL);
985 chain->activated = activate;
991 element_is_sink (GstElement * element)
995 GST_OBJECT_LOCK (element);
996 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
997 GST_OBJECT_UNLOCK (element);
999 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1004 element_has_property (GstElement * element, const gchar * pname, GType type)
1008 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1010 if (pspec == NULL) {
1011 GST_DEBUG_OBJECT (element, "no %s property", pname);
1015 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1016 g_type_is_a (pspec->value_type, type)) {
1017 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1018 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1022 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1023 "and we expected it to be of type %s", pname,
1024 g_type_name (pspec->value_type), g_type_name (type));
1031 const gchar *prop_name;
1034 } FindPropertyHelper;
1037 find_property (GstElement * element, FindPropertyHelper * helper)
1039 if (helper->need_sink && !element_is_sink (element)) {
1040 gst_object_unref (element);
1044 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1045 gst_object_unref (element);
1049 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1050 (helper->need_sink) ? "sink" : "element");
1051 return 0; /* keep it */
1054 /* FIXME: why not move these functions into core? */
1055 /* find a sink in the hierarchy with a property named @name. This function does
1056 * not increase the refcount of the returned object and thus remains valid as
1057 * long as the bin is valid. */
1059 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1060 const gchar * name, GType expected_type)
1062 GstElement *result = NULL;
1065 if (element_has_property (obj, name, expected_type)) {
1067 } else if (GST_IS_BIN (obj)) {
1068 FindPropertyHelper helper = { name, expected_type, TRUE };
1070 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1071 result = gst_iterator_find_custom (it,
1072 (GCompareFunc) find_property, &helper);
1073 gst_iterator_free (it);
1074 /* we don't need the extra ref */
1076 gst_object_unref (result);
1081 /* find an object in the hierarchy with a property named @name */
1083 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1084 const gchar * name, GType expected_type)
1086 GstElement *result = NULL;
1089 if (GST_IS_BIN (obj)) {
1090 FindPropertyHelper helper = { name, expected_type, FALSE };
1092 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1093 result = gst_iterator_find_custom (it,
1094 (GCompareFunc) find_property, &helper);
1095 gst_iterator_free (it);
1097 if (element_has_property (obj, name, expected_type)) {
1099 gst_object_ref (obj);
1106 do_async_start (GstPlaySink * playsink)
1108 GstMessage *message;
1110 if (!playsink->need_async_start) {
1111 GST_INFO_OBJECT (playsink, "no async_start needed");
1115 playsink->async_pending = TRUE;
1117 GST_INFO_OBJECT (playsink, "Sending async_start message");
1118 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1119 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1120 (playsink), message);
1124 do_async_done (GstPlaySink * playsink)
1126 GstMessage *message;
1128 if (playsink->async_pending) {
1129 GST_INFO_OBJECT (playsink, "Sending async_done message");
1130 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1131 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1132 (playsink), message);
1134 playsink->async_pending = FALSE;
1137 playsink->need_async_start = FALSE;
1140 /* try to change the state of an element. This function returns the element when
1141 * the state change could be performed. When this function returns NULL an error
1142 * occured and the element is unreffed if @unref is TRUE. */
1144 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1146 GstStateChangeReturn ret;
1149 ret = gst_element_set_state (element, GST_STATE_READY);
1150 if (ret == GST_STATE_CHANGE_FAILURE) {
1151 GST_DEBUG_OBJECT (playsink, "failed state change..");
1152 gst_element_set_state (element, GST_STATE_NULL);
1154 gst_object_unref (element);
1161 /* make the element (bin) that contains the elements needed to perform
1162 * video display. Only used for *raw* video streams.
1164 * +------------------------------------------------------------+
1166 * | +-------+ +----------+ +----------+ +---------+ |
1167 * | | queue | |colorspace| |videoscale| |videosink| |
1168 * | +-sink src-sink src-sink src-sink | |
1169 * | | +-------+ +----------+ +----------+ +---------+ |
1171 * +------------------------------------------------------------+
1174 static GstPlayVideoDeinterlaceChain *
1175 gen_video_deinterlace_chain (GstPlaySink * playsink)
1177 GstPlayVideoDeinterlaceChain *chain;
1180 GstElement *head = NULL, *prev = NULL;
1182 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1183 chain->chain.playsink = playsink;
1185 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1187 /* create a bin to hold objects, as we create them we add them to this bin so
1188 * that when something goes wrong we only need to unref the bin */
1189 chain->chain.bin = gst_bin_new ("vdbin");
1190 bin = GST_BIN_CAST (chain->chain.bin);
1191 gst_object_ref_sink (bin);
1193 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1194 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1195 if (chain->conv == NULL) {
1196 post_missing_element_message (playsink, COLORSPACE);
1197 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1198 (_("Missing element '%s' - check your GStreamer installation."),
1199 COLORSPACE), ("video rendering might fail"));
1201 gst_bin_add (bin, chain->conv);
1206 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1207 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1208 if (chain->deinterlace == NULL) {
1209 post_missing_element_message (playsink, "deinterlace");
1210 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1211 (_("Missing element '%s' - check your GStreamer installation."),
1212 "deinterlace"), ("deinterlacing won't work"));
1214 gst_bin_add (bin, chain->deinterlace);
1216 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1217 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1220 head = chain->deinterlace;
1222 prev = chain->deinterlace;
1226 pad = gst_element_get_static_pad (head, "sink");
1227 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1228 gst_object_unref (pad);
1230 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1234 pad = gst_element_get_static_pad (prev, "src");
1235 chain->srcpad = gst_ghost_pad_new ("src", pad);
1236 gst_object_unref (pad);
1238 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1241 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1242 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1248 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1249 (NULL), ("Failed to configure the video deinterlace chain."));
1250 free_chain ((GstPlayChain *) chain);
1255 /* make the element (bin) that contains the elements needed to perform
1258 * +------------------------------------------------------------+
1260 * | +-------+ +----------+ +----------+ +---------+ |
1261 * | | queue | |colorspace| |videoscale| |videosink| |
1262 * | +-sink src-sink src-sink src-sink | |
1263 * | | +-------+ +----------+ +----------+ +---------+ |
1265 * +------------------------------------------------------------+
1268 static GstPlayVideoChain *
1269 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1271 GstPlayVideoChain *chain;
1274 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1276 chain = g_new0 (GstPlayVideoChain, 1);
1277 chain->chain.playsink = playsink;
1278 chain->chain.raw = raw;
1280 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1282 if (playsink->video_sink) {
1283 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1284 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1286 /* only try fallback if no specific sink was chosen */
1287 if (chain->sink == NULL) {
1288 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1289 elem = gst_element_factory_make ("autovideosink", "videosink");
1290 chain->sink = try_element (playsink, elem, TRUE);
1292 if (chain->sink == NULL) {
1293 /* if default sink from config.h is different then try it too */
1294 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1295 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1296 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1297 chain->sink = try_element (playsink, elem, TRUE);
1301 playsink->video_sink = gst_object_ref (chain->sink);
1303 if (chain->sink == NULL)
1307 /* if we can disable async behaviour of the sink, we can avoid adding a
1308 * queue for the audio chain. */
1310 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1313 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1314 async, GST_ELEMENT_NAME (elem));
1315 g_object_set (elem, "async", async, NULL);
1316 chain->async = async;
1318 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1319 chain->async = TRUE;
1322 /* find ts-offset element */
1323 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1324 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1327 /* create a bin to hold objects, as we create them we add them to this bin so
1328 * that when something goes wrong we only need to unref the bin */
1329 chain->chain.bin = gst_bin_new ("vbin");
1330 bin = GST_BIN_CAST (chain->chain.bin);
1331 gst_object_ref_sink (bin);
1332 gst_bin_add (bin, chain->sink);
1334 /* decouple decoder from sink, this improves playback quite a lot since the
1335 * decoder can continue while the sink blocks for synchronisation. We don't
1336 * need a lot of buffers as this consumes a lot of memory and we don't want
1337 * too little because else we would be context switching too quickly. */
1338 chain->queue = gst_element_factory_make ("queue", "vqueue");
1339 if (chain->queue == NULL) {
1340 post_missing_element_message (playsink, "queue");
1341 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1342 (_("Missing element '%s' - check your GStreamer installation."),
1343 "queue"), ("video rendering might be suboptimal"));
1347 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1348 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1349 gst_bin_add (bin, chain->queue);
1350 head = prev = chain->queue;
1353 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1354 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1356 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1357 gst_bin_add (bin, chain->conv);
1359 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1360 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1369 GST_DEBUG_OBJECT (playsink, "linking to sink");
1370 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1371 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1375 pad = gst_element_get_static_pad (head, "sink");
1376 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1377 gst_object_unref (pad);
1379 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1386 if (!elem && !playsink->video_sink) {
1387 post_missing_element_message (playsink, "autovideosink");
1388 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1389 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1390 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1391 (_("Both autovideosink and %s elements are missing."),
1392 DEFAULT_VIDEOSINK), (NULL));
1394 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1395 (_("The autovideosink element is missing.")), (NULL));
1398 if (playsink->video_sink) {
1399 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1400 (_("Configured videosink %s is not working."),
1401 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1402 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1403 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1404 (_("Both autovideosink and %s elements are not working."),
1405 DEFAULT_VIDEOSINK), (NULL));
1407 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1408 (_("The autovideosink element is not working.")), (NULL));
1411 free_chain ((GstPlayChain *) chain);
1416 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1417 (NULL), ("Failed to configure the video sink."));
1418 /* checking sink made it READY */
1419 gst_element_set_state (chain->sink, GST_STATE_NULL);
1420 /* Remove chain from the bin to allow reuse later */
1421 gst_bin_remove (bin, chain->sink);
1422 free_chain ((GstPlayChain *) chain);
1428 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1431 GstPlayVideoChain *chain;
1432 GstStateChangeReturn ret;
1434 chain = playsink->videochain;
1436 chain->chain.raw = raw;
1438 /* if the chain was active we don't do anything */
1439 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1442 /* try to set the sink element to READY again */
1443 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1444 if (ret == GST_STATE_CHANGE_FAILURE)
1447 /* find ts-offset element */
1449 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1450 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1453 /* if we can disable async behaviour of the sink, we can avoid adding a
1454 * queue for the audio chain. */
1456 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1459 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1460 async, GST_ELEMENT_NAME (elem));
1461 g_object_set (elem, "async", async, NULL);
1462 chain->async = async;
1464 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1465 chain->async = TRUE;
1470 /* make an element for playback of video with subtitles embedded.
1471 * Only used for *raw* video streams.
1473 * +--------------------------------------------+
1475 * | +--------+ +-----------------+ |
1476 * | | queue | | subtitleoverlay | |
1477 * video--src sink---video_sink | |
1478 * | +--------+ | src--src
1479 * text------------------text_sink | |
1480 * | +-----------------+ |
1481 * +--------------------------------------------+
1484 static GstPlayTextChain *
1485 gen_text_chain (GstPlaySink * playsink)
1487 GstPlayTextChain *chain;
1490 GstPad *videosinkpad, *textsinkpad, *srcpad;
1492 chain = g_new0 (GstPlayTextChain, 1);
1493 chain->chain.playsink = playsink;
1495 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1497 chain->chain.bin = gst_bin_new ("tbin");
1498 bin = GST_BIN_CAST (chain->chain.bin);
1499 gst_object_ref_sink (bin);
1501 videosinkpad = textsinkpad = srcpad = NULL;
1503 /* first try to hook the text pad to the custom sink */
1504 if (playsink->text_sink) {
1505 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1506 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1509 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1512 /* make sure the sparse subtitles don't participate in the preroll */
1513 g_object_set (elem, "async", FALSE, NULL);
1514 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1515 gst_bin_add (bin, chain->sink);
1516 /* NOTE streamsynchronizer needs streams decoupled */
1517 /* make a little queue */
1518 chain->queue = gst_element_factory_make ("queue", "subqueue");
1519 if (chain->queue == NULL) {
1520 post_missing_element_message (playsink, "queue");
1521 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1522 (_("Missing element '%s' - check your GStreamer installation."),
1523 "queue"), ("rendering might be suboptimal"));
1525 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1526 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1527 "silent", TRUE, NULL);
1528 gst_bin_add (bin, chain->queue);
1530 /* we have a custom sink, this will be our textsinkpad */
1531 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1532 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1533 /* we're all fine now and we can add the sink to the chain */
1534 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1535 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1537 GST_WARNING_OBJECT (playsink,
1538 "can't find a sink pad on custom text sink");
1539 gst_bin_remove (bin, chain->sink);
1540 gst_bin_remove (bin, chain->queue);
1542 chain->queue = NULL;
1544 /* try to set sync to true but it's no biggie when we can't */
1545 if (chain->sink && (elem =
1546 gst_play_sink_find_property_sinks (playsink, chain->sink,
1547 "sync", G_TYPE_BOOLEAN)))
1548 g_object_set (elem, "sync", TRUE, NULL);
1551 gst_bin_remove (bin, chain->sink);
1553 GST_WARNING_OBJECT (playsink,
1554 "can't find async property in custom text sink");
1557 if (textsinkpad == NULL) {
1558 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1559 (_("Custom text sink element is not usable.")),
1560 ("fallback to default textoverlay"));
1564 if (textsinkpad == NULL) {
1565 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1566 /* make a little queue */
1567 chain->queue = gst_element_factory_make ("queue", "vqueue");
1568 if (chain->queue == NULL) {
1569 post_missing_element_message (playsink, "queue");
1570 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1571 (_("Missing element '%s' - check your GStreamer installation."),
1572 "queue"), ("video rendering might be suboptimal"));
1574 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1575 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1576 "silent", TRUE, NULL);
1577 gst_bin_add (bin, chain->queue);
1578 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1582 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1583 if (chain->overlay == NULL) {
1584 post_missing_element_message (playsink, "subtitleoverlay");
1585 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1586 (_("Missing element '%s' - check your GStreamer installation."),
1587 "subtitleoverlay"), ("subtitle rendering disabled"));
1589 GstElement *element;
1591 gst_bin_add (bin, chain->overlay);
1593 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1594 if (playsink->font_desc) {
1595 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1596 playsink->font_desc, NULL);
1598 if (playsink->subtitle_encoding) {
1599 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1600 playsink->subtitle_encoding, NULL);
1603 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1604 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1606 /* make another little queue to decouple streams */
1607 element = gst_element_factory_make ("queue", "subqueue");
1608 if (element == NULL) {
1609 post_missing_element_message (playsink, "queue");
1610 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1611 (_("Missing element '%s' - check your GStreamer installation."),
1612 "queue"), ("rendering might be suboptimal"));
1614 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1615 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1616 "silent", TRUE, NULL);
1617 gst_bin_add (bin, element);
1618 if (gst_element_link_pads_full (element, "src", chain->overlay,
1619 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1620 textsinkpad = gst_element_get_static_pad (element, "sink");
1621 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1623 gst_bin_remove (bin, chain->sink);
1624 gst_bin_remove (bin, chain->overlay);
1626 chain->overlay = NULL;
1627 gst_object_unref (videosinkpad);
1628 videosinkpad = NULL;
1635 if (videosinkpad == NULL) {
1636 /* if we still don't have a videosink, we don't have an overlay. the only
1637 * thing we can do is insert an identity and ghost the src
1639 chain->identity = gst_element_factory_make ("identity", "tidentity");
1640 if (chain->identity == NULL) {
1641 post_missing_element_message (playsink, "identity");
1642 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1643 (_("Missing element '%s' - check your GStreamer installation."),
1644 "identity"), (NULL));
1646 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1647 g_object_set (chain->identity, "silent", TRUE, NULL);
1648 gst_bin_add (bin, chain->identity);
1649 srcpad = gst_element_get_static_pad (chain->identity, "src");
1650 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1654 /* expose the ghostpads */
1656 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1657 gst_object_unref (videosinkpad);
1658 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1661 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1662 gst_object_unref (textsinkpad);
1663 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1666 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1667 gst_object_unref (srcpad);
1668 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1675 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1679 g_object_get (object, "volume", &vol, NULL);
1680 playsink->volume = vol;
1682 g_object_notify (G_OBJECT (playsink), "volume");
1686 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1690 g_object_get (object, "mute", &mute, NULL);
1691 playsink->mute = mute;
1693 g_object_notify (G_OBJECT (playsink), "mute");
1696 /* make the chain that contains the elements needed to perform
1699 * We add a tee as the first element so that we can link the visualisation chain
1700 * to it when requested.
1702 * +-------------------------------------------------------------+
1704 * | +---------+ +----------+ +---------+ +---------+ |
1705 * | |audioconv| |audioscale| | volume | |audiosink| |
1706 * | +-srck src-sink src-sink src-sink | |
1707 * | | +---------+ +----------+ +---------+ +---------+ |
1709 * +-------------------------------------------------------------+
1711 static GstPlayAudioChain *
1712 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1714 GstPlayAudioChain *chain;
1716 gboolean have_volume;
1718 GstElement *head, *prev, *elem = NULL;
1720 chain = g_new0 (GstPlayAudioChain, 1);
1721 chain->chain.playsink = playsink;
1722 chain->chain.raw = raw;
1724 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1726 if (playsink->audio_sink) {
1727 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1728 playsink->audio_sink);
1729 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1731 /* only try fallback if no specific sink was chosen */
1732 if (chain->sink == NULL) {
1733 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1734 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1735 chain->sink = try_element (playsink, elem, TRUE);
1737 if (chain->sink == NULL) {
1738 /* if default sink from config.h is different then try it too */
1739 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1740 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1741 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1742 chain->sink = try_element (playsink, elem, TRUE);
1746 playsink->audio_sink = gst_object_ref (chain->sink);
1748 if (chain->sink == NULL)
1751 chain->chain.bin = gst_bin_new ("abin");
1752 bin = GST_BIN_CAST (chain->chain.bin);
1753 gst_object_ref_sink (bin);
1754 gst_bin_add (bin, chain->sink);
1756 /* we have to add a queue when we need to decouple for the video sink in
1757 * visualisations and for streamsynchronizer */
1758 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1759 chain->queue = gst_element_factory_make ("queue", "aqueue");
1760 if (chain->queue == NULL) {
1761 post_missing_element_message (playsink, "queue");
1762 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1763 (_("Missing element '%s' - check your GStreamer installation."),
1764 "queue"), ("audio playback and visualizations might not work"));
1768 g_object_set (chain->queue, "silent", TRUE, NULL);
1769 gst_bin_add (bin, chain->queue);
1770 prev = head = chain->queue;
1773 /* find ts-offset element */
1774 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1775 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1778 /* check if the sink, or something within the sink, has the volume property.
1779 * If it does we don't need to add a volume element. */
1781 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1784 chain->volume = elem;
1786 g_signal_connect (chain->volume, "notify::volume",
1787 G_CALLBACK (notify_volume_cb), playsink);
1789 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1791 chain->sink_volume = TRUE;
1792 /* if the sink also has a mute property we can use this as well. We'll only
1793 * use the mute property if there is a volume property. We can simulate the
1794 * mute with the volume otherwise. */
1796 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1799 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1800 g_signal_connect (chain->mute, "notify::mute",
1801 G_CALLBACK (notify_mute_cb), playsink);
1803 /* use the sink to control the volume and mute */
1804 if (playsink->volume_changed) {
1805 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1806 playsink->volume_changed = FALSE;
1808 if (playsink->mute_changed) {
1810 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1813 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1815 playsink->mute_changed = FALSE;
1818 /* no volume, we need to add a volume element when we can */
1819 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1820 have_volume = FALSE;
1821 chain->sink_volume = FALSE;
1824 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1825 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1826 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1827 gboolean use_volume =
1828 !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME;
1829 GST_DEBUG_OBJECT (playsink,
1830 "creating audioconvert with use-converters %d, use-volume %d",
1831 use_converters, use_volume);
1833 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1834 "use-converters", use_converters, "use-volume", use_volume, NULL);
1835 gst_bin_add (bin, chain->conv);
1837 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1838 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1845 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1846 GstPlaySinkAudioConvert *conv =
1847 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1850 chain->volume = conv->volume;
1853 g_signal_connect (chain->volume, "notify::volume",
1854 G_CALLBACK (notify_volume_cb), playsink);
1856 /* volume also has the mute property */
1857 chain->mute = chain->volume;
1858 g_signal_connect (chain->mute, "notify::mute",
1859 G_CALLBACK (notify_mute_cb), playsink);
1861 /* configure with the latest volume and mute */
1862 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1864 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1870 /* we only have to link to the previous element if we have something in
1871 * front of the sink */
1872 GST_DEBUG_OBJECT (playsink, "linking to sink");
1873 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1874 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1878 /* post a warning if we have no way to configure the volume */
1880 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1881 (_("No volume control found")), ("Volume/mute is not available"));
1884 /* and ghost the sinkpad of the headmost element */
1885 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1886 pad = gst_element_get_static_pad (head, "sink");
1887 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1888 gst_object_unref (pad);
1889 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1896 if (!elem && !playsink->audio_sink) {
1897 post_missing_element_message (playsink, "autoaudiosink");
1898 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1899 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1900 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1901 (_("Both autoaudiosink and %s elements are missing."),
1902 DEFAULT_AUDIOSINK), (NULL));
1904 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1905 (_("The autoaudiosink element is missing.")), (NULL));
1908 if (playsink->audio_sink) {
1909 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1910 (_("Configured audiosink %s is not working."),
1911 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1912 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1913 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1914 (_("Both autoaudiosink and %s elements are not working."),
1915 DEFAULT_AUDIOSINK), (NULL));
1917 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1918 (_("The autoaudiosink element is not working.")), (NULL));
1921 free_chain ((GstPlayChain *) chain);
1926 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1927 (NULL), ("Failed to configure the audio sink."));
1928 /* checking sink made it READY */
1929 gst_element_set_state (chain->sink, GST_STATE_NULL);
1930 /* Remove chain from the bin to allow reuse later */
1931 gst_bin_remove (bin, chain->sink);
1932 free_chain ((GstPlayChain *) chain);
1938 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1941 GstPlayAudioChain *chain;
1942 GstStateChangeReturn ret;
1944 chain = playsink->audiochain;
1946 chain->chain.raw = raw;
1948 /* if the chain was active we don't do anything */
1949 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1952 /* try to set the sink element to READY again */
1953 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1954 if (ret == GST_STATE_CHANGE_FAILURE)
1957 /* find ts-offset element */
1958 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1959 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1962 /* check if the sink, or something within the sink, has the volume property.
1963 * If it does we don't need to add a volume element. */
1965 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1968 chain->volume = elem;
1970 if (playsink->volume_changed) {
1971 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1973 /* use the sink to control the volume */
1974 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1975 playsink->volume_changed = FALSE;
1978 g_signal_connect (chain->volume, "notify::volume",
1979 G_CALLBACK (notify_volume_cb), playsink);
1980 /* if the sink also has a mute property we can use this as well. We'll only
1981 * use the mute property if there is a volume property. We can simulate the
1982 * mute with the volume otherwise. */
1984 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1987 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1988 g_signal_connect (chain->mute, "notify::mute",
1989 G_CALLBACK (notify_mute_cb), playsink);
1992 g_object_set (chain->conv, "use-volume", FALSE, NULL);
1994 GstPlaySinkAudioConvert *conv =
1995 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1997 /* no volume, we need to add a volume element when we can */
1998 g_object_set (chain->conv, "use-volume", TRUE, NULL);
1999 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2001 /* Disconnect signals */
2002 disconnect_chain (chain, playsink);
2005 chain->volume = conv->volume;
2006 chain->mute = chain->volume;
2008 g_signal_connect (chain->volume, "notify::volume",
2009 G_CALLBACK (notify_volume_cb), playsink);
2011 g_signal_connect (chain->mute, "notify::mute",
2012 G_CALLBACK (notify_mute_cb), playsink);
2014 /* configure with the latest volume and mute */
2015 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2016 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2019 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2025 * +-------------------------------------------------------------------+
2027 * | +----------+ +------------+ +----------+ +-------+ |
2028 * | | visqueue | | audioconv | | audiores | | vis | |
2029 * | +-sink src-sink + samp src-sink src-sink src-+ |
2030 * | | +----------+ +------------+ +----------+ +-------+ | |
2032 * +-------------------------------------------------------------------+
2035 static GstPlayVisChain *
2036 gen_vis_chain (GstPlaySink * playsink)
2038 GstPlayVisChain *chain;
2044 chain = g_new0 (GstPlayVisChain, 1);
2045 chain->chain.playsink = playsink;
2047 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2049 chain->chain.bin = gst_bin_new ("visbin");
2050 bin = GST_BIN_CAST (chain->chain.bin);
2051 gst_object_ref_sink (bin);
2053 /* we're queuing raw audio here, we can remove this queue when we can disable
2054 * async behaviour in the video sink. */
2055 chain->queue = gst_element_factory_make ("queue", "visqueue");
2056 if (chain->queue == NULL)
2058 g_object_set (chain->queue, "silent", TRUE, NULL);
2059 gst_bin_add (bin, chain->queue);
2061 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2062 if (chain->conv == NULL)
2063 goto no_audioconvert;
2064 gst_bin_add (bin, chain->conv);
2066 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2067 if (chain->resample == NULL)
2068 goto no_audioresample;
2069 gst_bin_add (bin, chain->resample);
2071 /* this pad will be used for blocking the dataflow and switching the vis
2073 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2075 if (playsink->visualisation) {
2076 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2077 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2079 if (chain->vis == NULL) {
2080 GST_DEBUG_OBJECT (playsink, "trying goom");
2081 elem = gst_element_factory_make ("goom", "vis");
2082 chain->vis = try_element (playsink, elem, TRUE);
2084 if (chain->vis == NULL)
2087 gst_bin_add (bin, chain->vis);
2089 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2090 GST_PAD_LINK_CHECK_NOTHING);
2092 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2093 GST_PAD_LINK_CHECK_NOTHING);
2095 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2096 GST_PAD_LINK_CHECK_NOTHING);
2100 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2101 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2103 pad = gst_element_get_static_pad (chain->queue, "sink");
2104 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2105 gst_object_unref (pad);
2106 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2108 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2109 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2116 post_missing_element_message (playsink, "queue");
2117 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2118 (_("Missing element '%s' - check your GStreamer installation."),
2120 free_chain ((GstPlayChain *) chain);
2125 post_missing_element_message (playsink, "audioconvert");
2126 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2127 (_("Missing element '%s' - check your GStreamer installation."),
2128 "audioconvert"), ("possibly a liboil version mismatch?"));
2129 free_chain ((GstPlayChain *) chain);
2134 post_missing_element_message (playsink, "audioresample");
2135 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2136 (_("Missing element '%s' - check your GStreamer installation."),
2137 "audioresample"), (NULL));
2138 free_chain ((GstPlayChain *) chain);
2143 post_missing_element_message (playsink, "goom");
2144 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2145 (_("Missing element '%s' - check your GStreamer installation."),
2147 free_chain ((GstPlayChain *) chain);
2152 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2153 (NULL), ("Failed to configure the visualisation element."));
2154 /* element made it to READY */
2155 gst_element_set_state (chain->vis, GST_STATE_NULL);
2156 free_chain ((GstPlayChain *) chain);
2162 policy_pad_added_cb (GstElement * gstelement,
2163 GstPad * new_pad, gpointer user_data)
2167 if (gst_pad_get_direction (new_pad) == GST_PAD_SRC) {
2168 gst_object_ref (new_pad);
2174 _add_policy_and_synchronizer_pads (GstPlaySink * playsink,
2175 GstPad ** sinkpad_stream_synchronizer,
2176 GstPad ** srcpad_stream_synchronizer,
2177 GstPad ** sinkpad_policy,
2178 GstPad ** srcpad_policy)
2180 if (!*sinkpad_stream_synchronizer) {
2183 /* request stream synchronizer pads */
2184 *sinkpad_stream_synchronizer =
2185 gst_element_get_request_pad (GST_ELEMENT_CAST
2186 (playsink->stream_synchronizer), "sink_%d");
2187 it = gst_pad_iterate_internal_links (*sinkpad_stream_synchronizer);
2189 gst_iterator_next (it, (gpointer *)srcpad_stream_synchronizer);
2190 gst_iterator_free (it);
2192 /* request policy pads */
2193 g_signal_connect (G_OBJECT (playsink->policy), "pad-added",
2194 G_CALLBACK (policy_pad_added_cb), srcpad_policy);
2195 *sinkpad_policy = gst_element_get_request_pad (playsink->policy, "sink_%d");
2196 g_assert (*sinkpad_policy);
2197 g_signal_handlers_disconnect_by_func (playsink->policy,
2198 G_CALLBACK (policy_pad_added_cb), srcpad_policy);
2201 if (gst_pad_link (*srcpad_stream_synchronizer,
2202 *sinkpad_policy) != GST_PAD_LINK_OK) {
2203 GST_WARNING_OBJECT (playsink, "pad linking failed : (%s, %s)",
2204 GST_DEBUG_PAD_NAME (*srcpad_stream_synchronizer),
2205 GST_DEBUG_PAD_NAME (*sinkpad_policy));
2211 _remove_policy_and_synchronizer_pads (GstPlaySink * playsink,
2212 GstPad ** sinkpad_stream_synchronizer,
2213 GstPad ** srcpad_stream_synchronizer,
2214 GstPad ** sinkpad_policy,
2215 GstPad ** srcpad_policy)
2217 if (*sinkpad_stream_synchronizer) {
2218 gst_pad_unlink (*srcpad_stream_synchronizer, *sinkpad_policy);
2220 gst_element_release_request_pad (GST_ELEMENT_CAST
2221 (playsink->stream_synchronizer),
2222 *sinkpad_stream_synchronizer);
2224 gst_object_unref (*sinkpad_stream_synchronizer);
2225 *sinkpad_stream_synchronizer = NULL;
2227 gst_object_unref (*srcpad_stream_synchronizer);
2228 *srcpad_stream_synchronizer = NULL;
2230 gst_element_release_request_pad (playsink->policy, *sinkpad_policy);
2232 gst_object_unref (*sinkpad_policy);
2233 *sinkpad_policy = NULL;
2235 gst_object_unref (*srcpad_policy);
2236 *srcpad_policy = NULL;
2240 /* this function is called when all the request pads are requested and when we
2241 * have to construct the final pipeline. Based on the flags we construct the
2242 * final output pipelines.
2245 gst_play_sink_reconfigure (GstPlaySink * playsink)
2248 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2250 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2252 /* assume we need nothing */
2253 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2255 GST_PLAY_SINK_LOCK (playsink);
2256 GST_OBJECT_LOCK (playsink);
2257 /* get flags, there are protected with the object lock */
2258 flags = playsink->flags;
2259 GST_OBJECT_UNLOCK (playsink);
2261 /* figure out which components we need */
2262 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2263 /* we have subtitles and we are requested to show it */
2267 if (((flags & GST_PLAY_FLAG_VIDEO)
2268 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2269 /* we have video and we are requested to show it */
2272 /* we only deinterlace if native video is not requested and
2273 * we have raw video */
2274 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2275 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2276 need_deinterlace = TRUE;
2279 if (playsink->audio_pad) {
2280 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2283 if (playsink->audio_pad_raw) {
2284 /* only can do vis with raw uncompressed audio */
2285 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2286 /* also add video when we add visualisation */
2293 /* we have a text_pad and we need text rendering, in this case we need a
2294 * video_pad to combine the video with the text or visualizations */
2295 if (need_text && !need_video) {
2296 if (playsink->video_pad) {
2298 } else if (need_audio) {
2299 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2300 (_("Can't play a text file without video or visualizations.")),
2301 ("Have text pad but no video pad or visualizations"));
2304 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2305 (_("Can't play a text file without video or visualizations.")),
2306 ("Have text pad but no video pad or visualizations"));
2307 GST_PLAY_SINK_UNLOCK (playsink);
2312 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2313 need_video, need_vis, need_text);
2315 /* set up video pipeline */
2317 gboolean raw, async;
2319 /* we need a raw sink when we do vis or when we have a raw pad */
2320 raw = need_vis ? TRUE : playsink->video_pad_raw;
2321 /* we try to set the sink async=FALSE when we need vis, this way we can
2322 * avoid a queue in the audio chain. */
2325 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2326 playsink->video_pad_raw);
2328 if (playsink->videochain) {
2329 /* try to reactivate the chain */
2330 if (!setup_video_chain (playsink, raw, async)) {
2331 if (playsink->video_sinkpad_stream_synchronizer) {
2332 _remove_policy_and_synchronizer_pads (playsink,
2333 &playsink->video_sinkpad_stream_synchronizer,
2334 &playsink->video_srcpad_stream_synchronizer,
2335 &playsink->video_sinkpad_policy,
2336 &playsink->video_srcpad_policy);
2339 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2341 /* Remove the sink from the bin to keep its state
2342 * and unparent it to allow reuse */
2343 if (playsink->videochain->sink)
2344 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2345 playsink->videochain->sink);
2347 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2348 free_chain ((GstPlayChain *) playsink->videochain);
2349 playsink->videochain = NULL;
2353 if (!playsink->videochain)
2354 playsink->videochain = gen_video_chain (playsink, raw, async);
2355 if (!playsink->videochain)
2358 if (!playsink->video_sinkpad_stream_synchronizer) {
2359 _add_policy_and_synchronizer_pads (playsink,
2360 &playsink->video_sinkpad_stream_synchronizer,
2361 &playsink->video_srcpad_stream_synchronizer,
2362 &playsink->video_sinkpad_policy,
2363 &playsink->video_srcpad_policy);
2366 if (playsink->video_pad)
2367 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2368 playsink->video_sinkpad_stream_synchronizer);
2370 if (need_deinterlace) {
2371 if (!playsink->videodeinterlacechain)
2372 playsink->videodeinterlacechain =
2373 gen_video_deinterlace_chain (playsink);
2374 if (!playsink->videodeinterlacechain)
2377 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2379 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2381 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2382 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2384 gst_pad_link_full (playsink->video_srcpad_policy,
2385 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2387 if (playsink->videodeinterlacechain) {
2388 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2389 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2394 GST_DEBUG_OBJECT (playsink, "adding video chain");
2395 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2396 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2397 /* if we are not part of vis or subtitles, set the ghostpad target */
2398 if (!need_vis && !need_text && (!playsink->textchain
2399 || !playsink->text_pad)) {
2400 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2401 if (need_deinterlace)
2402 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2403 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2405 gst_pad_link_full (playsink->video_srcpad_policy,
2406 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2409 GST_DEBUG_OBJECT (playsink, "no video needed");
2410 if (playsink->videochain) {
2411 GST_DEBUG_OBJECT (playsink, "removing video chain");
2412 if (playsink->vischain) {
2415 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2417 /* also had visualisation, release the tee srcpad before we then
2418 * unlink the video from it */
2419 if (playsink->audio_tee_vissrc) {
2420 gst_element_release_request_pad (playsink->audio_tee,
2421 playsink->audio_tee_vissrc);
2422 gst_object_unref (playsink->audio_tee_vissrc);
2423 playsink->audio_tee_vissrc = NULL;
2426 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2427 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2430 if (playsink->video_sinkpad_stream_synchronizer) {
2431 _remove_policy_and_synchronizer_pads (playsink,
2432 &playsink->video_sinkpad_stream_synchronizer,
2433 &playsink->video_srcpad_stream_synchronizer,
2434 &playsink->video_sinkpad_policy,
2435 &playsink->video_srcpad_policy);
2438 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2439 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2440 if (playsink->videochain->ts_offset)
2441 gst_object_unref (playsink->videochain->ts_offset);
2442 playsink->videochain->ts_offset = NULL;
2445 if (playsink->videodeinterlacechain) {
2446 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2447 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2450 if (playsink->video_pad)
2451 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2457 GST_DEBUG_OBJECT (playsink, "adding audio");
2459 /* get a raw sink if we are asked for a raw pad */
2460 raw = playsink->audio_pad_raw;
2462 if (playsink->audiochain) {
2463 /* try to reactivate the chain */
2464 if (!setup_audio_chain (playsink, raw)) {
2465 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2466 if (playsink->audio_tee_asrc) {
2467 gst_element_release_request_pad (playsink->audio_tee,
2468 playsink->audio_tee_asrc);
2469 gst_object_unref (playsink->audio_tee_asrc);
2470 playsink->audio_tee_asrc = NULL;
2473 if (playsink->audio_sinkpad_stream_synchronizer) {
2474 _remove_policy_and_synchronizer_pads (playsink,
2475 &playsink->audio_sinkpad_stream_synchronizer,
2476 &playsink->audio_srcpad_stream_synchronizer,
2477 &playsink->audio_sinkpad_policy,
2478 &playsink->audio_srcpad_policy);
2481 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2483 /* Remove the sink from the bin to keep its state
2484 * and unparent it to allow reuse */
2485 if (playsink->audiochain->sink)
2486 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2487 playsink->audiochain->sink);
2489 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2490 disconnect_chain (playsink->audiochain, playsink);
2491 playsink->audiochain->volume = NULL;
2492 playsink->audiochain->mute = NULL;
2493 if (playsink->audiochain->ts_offset)
2494 gst_object_unref (playsink->audiochain->ts_offset);
2495 playsink->audiochain->ts_offset = NULL;
2496 free_chain ((GstPlayChain *) playsink->audiochain);
2497 playsink->audiochain = NULL;
2498 playsink->volume_changed = playsink->mute_changed = FALSE;
2502 if (!playsink->audiochain) {
2503 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2504 playsink->audiochain = gen_audio_chain (playsink, raw);
2507 if (!playsink->audio_sinkpad_stream_synchronizer) {
2508 _add_policy_and_synchronizer_pads (playsink,
2509 &playsink->audio_sinkpad_stream_synchronizer,
2510 &playsink->audio_srcpad_stream_synchronizer,
2511 &playsink->audio_sinkpad_policy,
2512 &playsink->audio_srcpad_policy);
2515 if (playsink->audiochain) {
2516 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2517 if (playsink->audio_tee_asrc == NULL) {
2518 playsink->audio_tee_asrc =
2519 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2521 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2522 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2523 gst_pad_link_full (playsink->audio_tee_asrc,
2524 playsink->audio_sinkpad_stream_synchronizer,
2525 GST_PAD_LINK_CHECK_NOTHING);
2526 gst_pad_link_full (playsink->audio_srcpad_policy,
2527 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2530 GST_DEBUG_OBJECT (playsink, "no audio needed");
2531 /* we have no audio or we are requested to not play audio */
2532 if (playsink->audiochain) {
2533 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2534 /* release the audio pad */
2535 if (playsink->audio_tee_asrc) {
2536 gst_element_release_request_pad (playsink->audio_tee,
2537 playsink->audio_tee_asrc);
2538 gst_object_unref (playsink->audio_tee_asrc);
2539 playsink->audio_tee_asrc = NULL;
2542 if (playsink->audio_sinkpad_stream_synchronizer) {
2543 _remove_policy_and_synchronizer_pads (playsink,
2544 &playsink->audio_sinkpad_stream_synchronizer,
2545 &playsink->audio_srcpad_stream_synchronizer,
2546 &playsink->audio_sinkpad_policy,
2547 &playsink->audio_srcpad_policy);
2550 if (playsink->audiochain->sink_volume) {
2551 disconnect_chain (playsink->audiochain, playsink);
2552 playsink->audiochain->volume = NULL;
2553 playsink->audiochain->mute = NULL;
2554 if (playsink->audiochain->ts_offset)
2555 gst_object_unref (playsink->audiochain->ts_offset);
2556 playsink->audiochain->ts_offset = NULL;
2558 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2559 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2566 if (!playsink->vischain)
2567 playsink->vischain = gen_vis_chain (playsink);
2569 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2571 if (playsink->vischain) {
2572 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2574 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2575 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2576 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2577 if (playsink->audio_tee_vissrc == NULL) {
2578 playsink->audio_tee_vissrc =
2579 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2581 gst_pad_link_full (playsink->audio_tee_vissrc,
2582 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2583 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2584 GST_PAD_LINK_CHECK_NOTHING);
2585 gst_pad_link_full (playsink->video_srcpad_policy,
2586 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2587 gst_object_unref (srcpad);
2590 GST_DEBUG_OBJECT (playsink, "no vis needed");
2591 if (playsink->vischain) {
2592 if (playsink->audio_tee_vissrc) {
2593 gst_element_release_request_pad (playsink->audio_tee,
2594 playsink->audio_tee_vissrc);
2595 gst_object_unref (playsink->audio_tee_vissrc);
2596 playsink->audio_tee_vissrc = NULL;
2598 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2599 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2600 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2605 GST_DEBUG_OBJECT (playsink, "adding text");
2606 if (!playsink->textchain) {
2607 GST_DEBUG_OBJECT (playsink, "creating text chain");
2608 playsink->textchain = gen_text_chain (playsink);
2610 if (playsink->textchain) {
2612 GST_DEBUG_OBJECT (playsink, "adding text chain");
2613 if (playsink->textchain->overlay)
2614 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2616 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2618 if (!playsink->text_sinkpad_stream_synchronizer) {
2619 _add_policy_and_synchronizer_pads (playsink,
2620 &playsink->text_sinkpad_stream_synchronizer,
2621 &playsink->text_srcpad_stream_synchronizer,
2622 &playsink->text_sinkpad_policy,
2623 &playsink->text_srcpad_policy);
2625 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2626 playsink->text_sinkpad_stream_synchronizer);
2627 gst_pad_link_full (playsink->text_srcpad_policy,
2628 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2635 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2636 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2637 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2638 GST_PAD_LINK_CHECK_NOTHING);
2639 gst_object_unref (srcpad);
2641 if (need_deinterlace)
2642 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2643 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2645 gst_pad_link_full (playsink->video_srcpad_policy,
2646 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2648 gst_pad_link_full (playsink->textchain->srcpad,
2649 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2651 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2654 GST_DEBUG_OBJECT (playsink, "no text needed");
2655 /* we have no subtitles/text or we are requested to not show them */
2657 if (playsink->text_sinkpad_stream_synchronizer) {
2658 _remove_policy_and_synchronizer_pads (playsink,
2659 &playsink->text_sinkpad_stream_synchronizer,
2660 &playsink->text_srcpad_stream_synchronizer,
2661 &playsink->text_sinkpad_policy,
2662 &playsink->text_srcpad_policy);
2665 if (playsink->textchain) {
2666 if (playsink->text_pad == NULL) {
2667 /* no text pad, remove the chain entirely */
2668 GST_DEBUG_OBJECT (playsink, "removing text chain");
2669 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2670 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2672 /* we have a chain and a textpad, turn the subtitles off */
2673 GST_DEBUG_OBJECT (playsink, "turning off the text");
2674 if (playsink->textchain->overlay)
2675 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2679 if (!need_video && playsink->video_pad) {
2680 if (playsink->video_sinkpad_stream_synchronizer) {
2681 _remove_policy_and_synchronizer_pads (playsink,
2682 &playsink->video_sinkpad_stream_synchronizer,
2683 &playsink->video_srcpad_stream_synchronizer,
2684 &playsink->video_sinkpad_policy,
2685 &playsink->video_srcpad_policy);
2688 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2691 if (playsink->text_pad && !playsink->textchain)
2692 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2694 update_av_offset (playsink);
2695 do_async_done (playsink);
2696 GST_PLAY_SINK_UNLOCK (playsink);
2703 /* gen_ chain already posted error */
2704 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2705 GST_PLAY_SINK_UNLOCK (playsink);
2711 * gst_play_sink_set_flags:
2712 * @playsink: a #GstPlaySink
2713 * @flags: #GstPlayFlags
2715 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2716 * when constructing the sink pipelins.
2718 * Returns: TRUE if the flags could be configured.
2721 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2723 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2725 GST_OBJECT_LOCK (playsink);
2726 playsink->flags = flags;
2727 GST_OBJECT_UNLOCK (playsink);
2733 * gst_play_sink_get_flags:
2734 * @playsink: a #GstPlaySink
2736 * Get the flags of @playsink. That flags control the behaviour of the sink when
2737 * it constructs the sink pipelines.
2739 * Returns: the currently configured #GstPlayFlags.
2742 gst_play_sink_get_flags (GstPlaySink * playsink)
2746 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2748 GST_OBJECT_LOCK (playsink);
2749 res = playsink->flags;
2750 GST_OBJECT_UNLOCK (playsink);
2756 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2758 GstPlayTextChain *chain;
2760 GST_PLAY_SINK_LOCK (playsink);
2761 chain = (GstPlayTextChain *) playsink->textchain;
2762 g_free (playsink->font_desc);
2763 playsink->font_desc = g_strdup (desc);
2764 if (chain && chain->overlay) {
2765 g_object_set (chain->overlay, "font-desc", desc, NULL);
2767 GST_PLAY_SINK_UNLOCK (playsink);
2771 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2773 gchar *result = NULL;
2774 GstPlayTextChain *chain;
2776 GST_PLAY_SINK_LOCK (playsink);
2777 chain = (GstPlayTextChain *) playsink->textchain;
2778 if (chain && chain->overlay) {
2779 g_object_get (chain->overlay, "font-desc", &result, NULL);
2780 playsink->font_desc = g_strdup (result);
2782 result = g_strdup (playsink->font_desc);
2784 GST_PLAY_SINK_UNLOCK (playsink);
2790 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2791 const gchar * encoding)
2793 GstPlayTextChain *chain;
2795 GST_PLAY_SINK_LOCK (playsink);
2796 chain = (GstPlayTextChain *) playsink->textchain;
2797 g_free (playsink->subtitle_encoding);
2798 playsink->subtitle_encoding = g_strdup (encoding);
2799 if (chain && chain->overlay) {
2800 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2802 GST_PLAY_SINK_UNLOCK (playsink);
2806 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2808 gchar *result = NULL;
2809 GstPlayTextChain *chain;
2811 GST_PLAY_SINK_LOCK (playsink);
2812 chain = (GstPlayTextChain *) playsink->textchain;
2813 if (chain && chain->overlay) {
2814 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2815 playsink->subtitle_encoding = g_strdup (result);
2817 result = g_strdup (playsink->subtitle_encoding);
2819 GST_PLAY_SINK_UNLOCK (playsink);
2825 update_av_offset (GstPlaySink * playsink)
2828 GstPlayAudioChain *achain;
2829 GstPlayVideoChain *vchain;
2831 av_offset = playsink->av_offset;
2832 achain = (GstPlayAudioChain *) playsink->audiochain;
2833 vchain = (GstPlayVideoChain *) playsink->videochain;
2835 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2836 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2837 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2839 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2844 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2846 GST_PLAY_SINK_LOCK (playsink);
2847 playsink->av_offset = av_offset;
2848 update_av_offset (playsink);
2849 GST_PLAY_SINK_UNLOCK (playsink);
2853 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2857 GST_PLAY_SINK_LOCK (playsink);
2858 result = playsink->av_offset;
2859 GST_PLAY_SINK_UNLOCK (playsink);
2865 * gst_play_sink_get_last_frame:
2866 * @playsink: a #GstPlaySink
2868 * Get the last displayed frame from @playsink. This frame is in the native
2869 * format of the sink element, the caps on the result buffer contain the format
2870 * of the frame data.
2872 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2876 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2878 GstBuffer *result = NULL;
2879 GstPlayVideoChain *chain;
2881 GST_PLAY_SINK_LOCK (playsink);
2882 GST_DEBUG_OBJECT (playsink, "taking last frame");
2883 /* get the video chain if we can */
2884 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2885 GST_DEBUG_OBJECT (playsink, "found video chain");
2886 /* see if the chain is active */
2887 if (chain->chain.activated && chain->sink) {
2890 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2892 /* find and get the last-buffer property now */
2894 gst_play_sink_find_property (playsink, chain->sink,
2895 "last-buffer", GST_TYPE_BUFFER))) {
2896 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2897 g_object_get (elem, "last-buffer", &result, NULL);
2898 gst_object_unref (elem);
2902 GST_PLAY_SINK_UNLOCK (playsink);
2908 * gst_play_sink_convert_frame:
2909 * @playsink: a #GstPlaySink
2912 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2913 * be in the native format of the sink element and the caps on the buffer
2914 * describe the format of the frame. If @caps is not %NULL, the video
2915 * frame will be converted to the format of the caps.
2917 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2918 * available or when the conversion failed.
2921 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2925 result = gst_play_sink_get_last_frame (playsink);
2926 if (result != NULL && caps != NULL) {
2930 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2931 gst_buffer_unref (result);
2932 if (temp == NULL && err) {
2933 /* I'm really uncertain whether we should make playsink post an error
2934 * on the bus or not. It's not like it's a critical issue regarding
2935 * playsink behaviour. */
2936 GST_ERROR ("Error converting frame: %s", err->message);
2945 is_raw_structure (GstStructure * s)
2949 name = gst_structure_get_name (s);
2951 if (g_str_has_prefix (name, "video/x-raw-") ||
2952 g_str_has_prefix (name, "audio/x-raw-"))
2958 is_raw_pad (GstPad * pad)
2960 GstPad *peer = gst_pad_get_peer (pad);
2962 gboolean raw = TRUE;
2967 caps = gst_pad_get_negotiated_caps (peer);
2971 caps = gst_pad_get_caps_reffed (peer);
2973 n = gst_caps_get_size (caps);
2974 for (i = 0; i < n; i++) {
2975 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2979 } else if (raw != r) {
2980 GST_ERROR_OBJECT (pad,
2981 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2987 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2989 gst_caps_unref (caps);
2990 gst_object_unref (peer);
2996 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
2998 GstPlaySink *playsink = (GstPlaySink *) user_data;
3001 GST_PLAY_SINK_LOCK (playsink);
3003 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3004 if (pad == playsink->video_pad) {
3005 playsink->video_pad_blocked = blocked;
3006 GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
3008 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3009 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3011 } else if (pad == playsink->audio_pad) {
3012 playsink->audio_pad_blocked = blocked;
3013 GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
3015 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3016 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3018 } else if (pad == playsink->text_pad) {
3019 playsink->text_pad_blocked = blocked;
3020 GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
3022 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3026 gst_object_unref (pad);
3027 GST_PLAY_SINK_UNLOCK (playsink);
3031 /* We reconfigure when for ALL streams:
3032 * * there isn't a pad
3033 * * OR the pad is blocked
3034 * * OR there are no pending blocks on that pad
3037 if ((!playsink->video_pad || playsink->video_pad_blocked
3038 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3039 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3040 && (!playsink->text_pad || playsink->text_pad_blocked
3041 || !PENDING_TEXT_BLOCK (playsink))) {
3042 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3044 if (playsink->video_pad) {
3045 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3046 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3047 playsink->video_pad_raw);
3050 if (playsink->audio_pad) {
3051 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3052 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3053 playsink->audio_pad_raw);
3056 gst_play_sink_reconfigure (playsink);
3058 if (playsink->video_pad) {
3060 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3061 (playsink->video_pad)));
3062 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3063 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3064 gst_object_unref (opad);
3067 if (playsink->audio_pad) {
3069 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3070 (playsink->audio_pad)));
3071 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3072 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3073 gst_object_unref (opad);
3076 if (playsink->text_pad) {
3078 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3079 (playsink->text_pad)));
3080 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3081 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3082 gst_object_unref (opad);
3086 gst_object_unref (pad);
3088 GST_PLAY_SINK_UNLOCK (playsink);
3092 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3094 gboolean reconfigure = FALSE;
3098 g_object_get (pad, "caps", &caps, NULL);
3102 if (pad == playsink->audio_pad) {
3103 raw = is_raw_pad (pad);
3104 reconfigure = (!!playsink->audio_pad_raw != !!raw)
3105 && playsink->audiochain;
3106 GST_DEBUG_OBJECT (pad,
3107 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3109 } else if (pad == playsink->video_pad) {
3110 raw = is_raw_pad (pad);
3111 reconfigure = (!!playsink->video_pad_raw != !!raw)
3112 && playsink->videochain;
3113 GST_DEBUG_OBJECT (pad,
3114 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3118 gst_caps_unref (caps);
3121 GST_PLAY_SINK_LOCK (playsink);
3122 if (playsink->video_pad) {
3124 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3125 (playsink->video_pad)));
3126 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3127 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3128 gst_object_unref (opad);
3131 if (playsink->audio_pad) {
3133 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3134 (playsink->audio_pad)));
3135 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3136 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3137 gst_object_unref (opad);
3140 if (playsink->text_pad) {
3142 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3143 (playsink->text_pad)));
3144 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3145 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3146 gst_object_unref (opad);
3148 GST_PLAY_SINK_UNLOCK (playsink);
3153 * gst_play_sink_request_pad
3154 * @playsink: a #GstPlaySink
3155 * @type: a #GstPlaySinkType
3157 * Create or return a pad of @type.
3159 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3162 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3165 gboolean created = FALSE;
3166 gboolean activate = TRUE;
3167 const gchar *pad_name = NULL;
3169 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3171 GST_PLAY_SINK_LOCK (playsink);
3173 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3174 case GST_PLAY_SINK_TYPE_AUDIO:
3175 pad_name = "audio_sink";
3176 if (!playsink->audio_tee) {
3177 GST_LOG_OBJECT (playsink, "creating tee");
3178 /* create tee when needed. This element will feed the audio sink chain
3179 * and the vis chain. */
3180 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3181 if (playsink->audio_tee == NULL) {
3182 post_missing_element_message (playsink, "tee");
3183 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3184 (_("Missing element '%s' - check your GStreamer installation."),
3189 playsink->audio_tee_sink =
3190 gst_element_get_static_pad (playsink->audio_tee, "sink");
3191 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3192 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3195 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3197 if (!playsink->audio_pad) {
3198 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3199 playsink->audio_pad =
3200 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3201 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3202 G_CALLBACK (caps_notify_cb), playsink);
3205 playsink->audio_pad_raw = FALSE;
3206 res = playsink->audio_pad;
3208 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3209 case GST_PLAY_SINK_TYPE_VIDEO:
3210 pad_name = "video_sink";
3211 if (!playsink->video_pad) {
3212 GST_LOG_OBJECT (playsink, "ghosting videosink");
3213 playsink->video_pad =
3214 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3215 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3216 G_CALLBACK (caps_notify_cb), playsink);
3219 playsink->video_pad_raw = FALSE;
3220 res = playsink->video_pad;
3222 case GST_PLAY_SINK_TYPE_TEXT:
3223 GST_LOG_OBJECT (playsink, "ghosting text");
3224 if (!playsink->text_pad) {
3225 playsink->text_pad =
3226 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3229 res = playsink->text_pad;
3231 case GST_PLAY_SINK_TYPE_FLUSHING:
3235 /* we need a unique padname for the flushing pad. */
3236 padname = g_strdup_printf ("flushing_%d", playsink->count);
3237 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3248 GST_PLAY_SINK_UNLOCK (playsink);
3250 if (created && res) {
3251 /* we have to add the pad when it's active or we get an error when the
3252 * element is 'running' */
3253 gst_pad_set_active (res, TRUE);
3254 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3255 if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3257 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3259 gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3260 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3261 PENDING_FLAG_SET (playsink, type);
3262 gst_object_unref (blockpad);
3265 gst_pad_set_active (res, activate);
3272 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3277 GstPlaySinkType type;
3278 const gchar *tplname;
3280 g_return_val_if_fail (templ != NULL, NULL);
3282 GST_DEBUG_OBJECT (element, "name:%s", name);
3284 psink = GST_PLAY_SINK (element);
3285 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3287 /* Figure out the GstPlaySinkType based on the template */
3288 if (!strcmp (tplname, "audio_sink"))
3289 type = GST_PLAY_SINK_TYPE_AUDIO;
3290 else if (!strcmp (tplname, "audio_raw_sink"))
3291 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3292 else if (!strcmp (tplname, "video_sink"))
3293 type = GST_PLAY_SINK_TYPE_VIDEO;
3294 else if (!strcmp (tplname, "video_raw_sink"))
3295 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3296 else if (!strcmp (tplname, "text_sink"))
3297 type = GST_PLAY_SINK_TYPE_TEXT;
3299 goto unknown_template;
3301 pad = gst_play_sink_request_pad (psink, type);
3305 GST_WARNING_OBJECT (element, "Unknown pad template");
3310 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3312 GstPad **res = NULL;
3313 gboolean untarget = TRUE;
3315 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3317 GST_PLAY_SINK_LOCK (playsink);
3318 if (pad == playsink->video_pad) {
3319 res = &playsink->video_pad;
3320 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3322 } else if (pad == playsink->audio_pad) {
3323 res = &playsink->audio_pad;
3324 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3326 } else if (pad == playsink->text_pad) {
3327 res = &playsink->text_pad;
3329 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3333 GST_PLAY_SINK_UNLOCK (playsink);
3336 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3337 gst_pad_set_active (*res, FALSE);
3339 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3340 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3342 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3343 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3349 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3351 GstPlaySink *psink = GST_PLAY_SINK (element);
3353 gst_play_sink_release_pad (psink, pad);
3357 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3359 GstPlaySink *playsink;
3361 playsink = GST_PLAY_SINK_CAST (bin);
3363 switch (GST_MESSAGE_TYPE (message)) {
3364 case GST_MESSAGE_STEP_DONE:
3369 gboolean flush, intermediate, eos;
3372 GST_INFO_OBJECT (playsink, "Handling step-done message");
3373 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3374 &intermediate, &duration, &eos);
3376 if (format == GST_FORMAT_BUFFERS) {
3377 /* for the buffer format, we align the other streams */
3378 if (playsink->audiochain) {
3382 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3385 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3386 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3390 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3394 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3399 /* Send an event to our sinks until one of them works; don't then send to the
3400 * remaining sinks (unlike GstBin)
3401 * Special case: If a text sink is set we need to send the event
3402 * to them in case it's source is different from the a/v stream's source.
3405 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3407 gboolean res = TRUE;
3409 if (playsink->textchain && playsink->textchain->sink) {
3410 gst_event_ref (event);
3411 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3412 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3414 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3418 if (playsink->videochain) {
3419 gst_event_ref (event);
3420 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3421 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3424 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3426 if (playsink->audiochain) {
3427 gst_event_ref (event);
3428 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3429 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3432 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3436 gst_event_unref (event);
3440 /* We only want to send the event to a single sink (overriding GstBin's
3441 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3442 * events appropriately. So, this is a messy duplication of code. */
3444 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3446 gboolean res = FALSE;
3447 GstEventType event_type = GST_EVENT_TYPE (event);
3448 GstPlaySink *playsink;
3450 playsink = GST_PLAY_SINK_CAST (element);
3452 switch (event_type) {
3453 case GST_EVENT_SEEK:
3454 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3455 res = gst_play_sink_send_event_to_sink (playsink, event);
3457 case GST_EVENT_STEP:
3462 gboolean flush, intermediate;
3464 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3467 if (format == GST_FORMAT_BUFFERS) {
3468 /* for buffers, we will try to step video frames, for other formats we
3469 * send the step to all sinks */
3470 res = gst_play_sink_send_event_to_sink (playsink, event);
3473 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3480 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3487 static GstStateChangeReturn
3488 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3490 GstStateChangeReturn ret;
3491 GstStateChangeReturn bret;
3493 GstPlaySink *playsink;
3495 playsink = GST_PLAY_SINK (element);
3497 switch (transition) {
3498 case GST_STATE_CHANGE_READY_TO_PAUSED:
3499 playsink->need_async_start = TRUE;
3500 /* we want to go async to PAUSED until we managed to configure and add the
3502 do_async_start (playsink);
3503 ret = GST_STATE_CHANGE_ASYNC;
3505 case GST_STATE_CHANGE_PAUSED_TO_READY:
3506 /* unblock all pads here */
3507 GST_PLAY_SINK_LOCK (playsink);
3508 if (playsink->video_pad) {
3510 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3511 (playsink->video_pad)));
3512 if (gst_pad_is_blocked (opad)) {
3513 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3514 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3516 gst_object_unref (opad);
3517 playsink->video_pad_blocked = FALSE;
3520 if (playsink->audio_pad) {
3522 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3523 (playsink->audio_pad)));
3525 if (gst_pad_is_blocked (opad)) {
3526 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3527 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3529 gst_object_unref (opad);
3530 playsink->audio_pad_blocked = FALSE;
3533 if (playsink->text_pad) {
3535 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3536 (playsink->text_pad)));
3537 if (gst_pad_is_blocked (opad)) {
3538 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3539 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3541 gst_object_unref (opad);
3542 playsink->text_pad_blocked = FALSE;
3544 GST_PLAY_SINK_UNLOCK (playsink);
3546 case GST_STATE_CHANGE_READY_TO_NULL:
3547 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3548 /* remove our links to the mute and volume elements when they were
3549 * provided by a sink */
3550 disconnect_chain (playsink->audiochain, playsink);
3551 playsink->audiochain->volume = NULL;
3552 playsink->audiochain->mute = NULL;
3555 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3556 gst_object_unref (playsink->audiochain->ts_offset);
3557 playsink->audiochain->ts_offset = NULL;
3560 if (playsink->videochain && playsink->videochain->ts_offset) {
3561 gst_object_unref (playsink->videochain->ts_offset);
3562 playsink->videochain->ts_offset = NULL;
3564 ret = GST_STATE_CHANGE_SUCCESS;
3567 /* all other state changes return SUCCESS by default, this value can be
3568 * overridden by the result of the children */
3569 ret = GST_STATE_CHANGE_SUCCESS;
3573 /* do the state change of the children */
3575 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3577 /* now look at the result of our children and adjust the return value */
3579 case GST_STATE_CHANGE_FAILURE:
3580 /* failure, we stop */
3581 goto activate_failed;
3582 case GST_STATE_CHANGE_NO_PREROLL:
3583 /* some child returned NO_PREROLL. This is strange but we never know. We
3584 * commit our async state change (if any) and return the NO_PREROLL */
3585 do_async_done (playsink);
3588 case GST_STATE_CHANGE_ASYNC:
3589 /* some child was async, return this */
3593 /* return our previously configured return value */
3597 switch (transition) {
3598 case GST_STATE_CHANGE_READY_TO_PAUSED:
3600 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3601 /* FIXME Release audio device when we implement that */
3602 playsink->need_async_start = TRUE;
3604 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3605 if (playsink->video_sinkpad_stream_synchronizer) {
3606 _remove_policy_and_synchronizer_pads (playsink,
3607 &playsink->video_sinkpad_stream_synchronizer,
3608 &playsink->video_srcpad_stream_synchronizer,
3609 &playsink->video_sinkpad_policy,
3610 &playsink->video_srcpad_policy);
3612 if (playsink->audio_sinkpad_stream_synchronizer) {
3613 _remove_policy_and_synchronizer_pads (playsink,
3614 &playsink->audio_sinkpad_stream_synchronizer,
3615 &playsink->audio_srcpad_stream_synchronizer,
3616 &playsink->audio_sinkpad_policy,
3617 &playsink->audio_srcpad_policy);
3619 if (playsink->text_sinkpad_stream_synchronizer) {
3620 _remove_policy_and_synchronizer_pads (playsink,
3621 &playsink->text_sinkpad_stream_synchronizer,
3622 &playsink->text_srcpad_stream_synchronizer,
3623 &playsink->text_sinkpad_policy,
3624 &playsink->text_srcpad_policy);
3628 case GST_STATE_CHANGE_READY_TO_NULL:
3629 /* remove sinks we added */
3630 if (playsink->videodeinterlacechain) {
3631 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3633 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3635 if (playsink->videochain) {
3636 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3637 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3639 if (playsink->audiochain) {
3640 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3641 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3643 if (playsink->vischain) {
3644 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3645 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3647 if (playsink->textchain) {
3648 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3649 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3651 do_async_done (playsink);
3652 /* when going to READY, keep elements around as long as possible,
3653 * so they may be re-used faster next time/url around.
3654 * when really going to NULL, clean up everything completely. */
3655 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3657 /* Unparent the sinks to allow reuse */
3658 if (playsink->videochain && playsink->videochain->sink)
3659 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3660 playsink->videochain->sink);
3661 if (playsink->audiochain && playsink->audiochain->sink)
3662 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3663 playsink->audiochain->sink);
3664 if (playsink->textchain && playsink->textchain->sink)
3665 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3666 playsink->textchain->sink);
3668 if (playsink->audio_sink != NULL)
3669 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3670 if (playsink->video_sink != NULL)
3671 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3672 if (playsink->visualisation != NULL)
3673 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3674 if (playsink->text_sink != NULL)
3675 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3677 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3678 playsink->videodeinterlacechain = NULL;
3679 free_chain ((GstPlayChain *) playsink->videochain);
3680 playsink->videochain = NULL;
3681 free_chain ((GstPlayChain *) playsink->audiochain);
3682 playsink->audiochain = NULL;
3683 free_chain ((GstPlayChain *) playsink->vischain);
3684 playsink->vischain = NULL;
3685 free_chain ((GstPlayChain *) playsink->textchain);
3686 playsink->textchain = NULL;
3697 GST_DEBUG_OBJECT (element,
3698 "element failed to change states -- activation problem?");
3699 return GST_STATE_CHANGE_FAILURE;
3704 gst_play_sink_set_property (GObject * object, guint prop_id,
3705 const GValue * value, GParamSpec * spec)
3707 GstPlaySink *playsink = GST_PLAY_SINK (object);
3711 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3714 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3717 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3719 case PROP_FONT_DESC:
3720 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3722 case PROP_SUBTITLE_ENCODING:
3723 gst_play_sink_set_subtitle_encoding (playsink,
3724 g_value_get_string (value));
3726 case PROP_VIS_PLUGIN:
3727 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3729 case PROP_AV_OFFSET:
3730 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3732 case PROP_VIDEO_SINK:
3733 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3734 g_value_get_object (value));
3736 case PROP_AUDIO_SINK:
3737 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3738 g_value_get_object (value));
3740 case PROP_TEXT_SINK:
3741 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3742 g_value_get_object (value));
3745 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3751 gst_play_sink_get_property (GObject * object, guint prop_id,
3752 GValue * value, GParamSpec * spec)
3754 GstPlaySink *playsink = GST_PLAY_SINK (object);
3758 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3761 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3764 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3766 case PROP_FONT_DESC:
3767 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3769 case PROP_SUBTITLE_ENCODING:
3770 g_value_take_string (value,
3771 gst_play_sink_get_subtitle_encoding (playsink));
3773 case PROP_VIS_PLUGIN:
3774 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3777 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3779 case PROP_AV_OFFSET:
3780 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3782 case PROP_VIDEO_SINK:
3783 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3784 GST_PLAY_SINK_TYPE_VIDEO));
3786 case PROP_AUDIO_SINK:
3787 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3788 GST_PLAY_SINK_TYPE_AUDIO));
3790 case PROP_TEXT_SINK:
3791 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3792 GST_PLAY_SINK_TYPE_TEXT));
3795 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3802 gst_play_sink_plugin_init (GstPlugin * plugin)
3804 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3806 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3807 GST_TYPE_PLAY_SINK);