2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
28 #include <gst/gst-i18n-plugin.h>
29 #include <gst/pbutils/pbutils.h>
30 #include <gst/video/video.h>
32 #include "gstplaysink.h"
33 #include "gststreamsynchronizer.h"
34 #include "gstplaysinkvideoconvert.h"
35 #include "gstplaysinkaudioconvert.h"
37 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
38 #define GST_CAT_DEFAULT gst_play_sink_debug
40 #define VOLUME_MAX_DOUBLE 10.0
42 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
43 GST_PLAY_FLAG_SOFT_VOLUME
45 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
47 /* holds the common data fields for the audio and video pipelines. We keep them
48 * in a structure to more easily have all the info available. */
51 GstPlaySink *playsink;
64 GstElement *volume; /* element with the volume property */
65 gboolean sink_volume; /* if the volume was provided by the sink */
66 GstElement *mute; /* element with the mute property */
68 GstElement *ts_offset;
74 GstPad *sinkpad, *srcpad;
76 GstElement *deinterlace;
77 } GstPlayVideoDeinterlaceChain;
87 GstElement *ts_offset;
97 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
98 GstPad *vissinkpad; /* visualisation sinkpad, */
100 GstPad *vissrcpad; /* visualisation srcpad, */
101 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
110 GstElement *identity;
112 GstPad *videosinkpad;
114 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
116 GstElement *sink; /* custom sink to receive subtitle buffers */
119 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
120 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
121 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
122 g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
123 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
125 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
126 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
127 g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
130 #define PENDING_FLAG_SET(playsink, flagtype) \
131 ((playsink->pending_blocked_pads) |= (1 << flagtype))
132 #define PENDING_FLAG_UNSET(playsink, flagtype) \
133 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
134 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
135 ((playsink->pending_blocked_pads) & (1 << flagtype))
136 #define PENDING_VIDEO_BLOCK(playsink) \
137 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
138 #define PENDING_AUDIO_BLOCK(playsink) \
139 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
140 #define PENDING_TEXT_BLOCK(playsink) \
141 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
147 GStaticRecMutex lock;
149 gboolean async_pending;
150 gboolean need_async_start;
154 GstStreamSynchronizer *stream_synchronizer;
157 GstPlayAudioChain *audiochain;
158 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
159 GstPlayVideoChain *videochain;
160 GstPlayVisChain *vischain;
161 GstPlayTextChain *textchain;
165 gboolean audio_pad_raw;
166 gboolean audio_pad_blocked;
167 GstPad *audio_srcpad_stream_synchronizer;
168 GstPad *audio_sinkpad_stream_synchronizer;
170 GstElement *audio_tee;
171 GstPad *audio_tee_sink;
172 GstPad *audio_tee_asrc;
173 GstPad *audio_tee_vissrc;
176 gboolean video_pad_raw;
177 gboolean video_pad_blocked;
178 GstPad *video_srcpad_stream_synchronizer;
179 GstPad *video_sinkpad_stream_synchronizer;
182 gboolean text_pad_blocked;
183 GstPad *text_srcpad_stream_synchronizer;
184 GstPad *text_sinkpad_stream_synchronizer;
186 guint32 pending_blocked_pads;
189 GstElement *audio_sink;
190 GstElement *video_sink;
191 GstElement *visualisation;
192 GstElement *text_sink;
195 gchar *font_desc; /* font description */
196 gchar *subtitle_encoding; /* subtitle encoding */
197 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
199 gboolean volume_changed; /* volume/mute changed while no audiochain */
200 gboolean mute_changed; /* ... has been created yet */
204 struct _GstPlaySinkClass
206 GstBinClass parent_class;
208 gboolean (*reconfigure) (GstPlaySink * playsink);
210 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
214 static GstStaticPadTemplate audiotemplate =
215 GST_STATIC_PAD_TEMPLATE ("audio_sink",
218 GST_STATIC_CAPS_ANY);
219 static GstStaticPadTemplate videotemplate =
220 GST_STATIC_PAD_TEMPLATE ("video_sink",
223 GST_STATIC_CAPS_ANY);
224 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
227 GST_STATIC_CAPS_ANY);
229 /* FIXME 0.11: Remove */
230 static GstStaticPadTemplate audiorawtemplate =
231 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
234 GST_STATIC_CAPS_ANY);
235 static GstStaticPadTemplate videorawtemplate =
236 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
239 GST_STATIC_CAPS_ANY);
250 PROP_SUBTITLE_ENCODING,
266 static void gst_play_sink_dispose (GObject * object);
267 static void gst_play_sink_finalize (GObject * object);
268 static void gst_play_sink_set_property (GObject * object, guint prop_id,
269 const GValue * value, GParamSpec * spec);
270 static void gst_play_sink_get_property (GObject * object, guint prop_id,
271 GValue * value, GParamSpec * spec);
273 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
274 GstPadTemplate * templ, const gchar * name);
275 static void gst_play_sink_release_request_pad (GstElement * element,
277 static gboolean gst_play_sink_send_event (GstElement * element,
279 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
280 GstStateChange transition);
282 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
284 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
285 GstPlaySink * playsink);
286 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
287 GstPlaySink * playsink);
289 static void update_av_offset (GstPlaySink * playsink);
292 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
293 GValue * return_value G_GNUC_UNUSED,
294 guint n_param_values,
295 const GValue * param_values,
296 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
298 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
299 gpointer arg_1, gpointer data2);
300 register GMarshalFunc_OBJECT__BOXED callback;
301 register GCClosure *cc = (GCClosure *) closure;
302 register gpointer data1, data2;
304 g_return_if_fail (return_value != NULL);
305 g_return_if_fail (n_param_values == 2);
307 if (G_CCLOSURE_SWAP_DATA (closure)) {
308 data1 = closure->data;
309 data2 = g_value_peek_pointer (param_values + 0);
311 data1 = g_value_peek_pointer (param_values + 0);
312 data2 = closure->data;
315 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
317 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
319 gst_value_take_buffer (return_value, v_return);
322 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
324 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
327 gst_play_sink_class_init (GstPlaySinkClass * klass)
329 GObjectClass *gobject_klass;
330 GstElementClass *gstelement_klass;
331 GstBinClass *gstbin_klass;
333 gobject_klass = (GObjectClass *) klass;
334 gstelement_klass = (GstElementClass *) klass;
335 gstbin_klass = (GstBinClass *) klass;
337 gobject_klass->dispose = gst_play_sink_dispose;
338 gobject_klass->finalize = gst_play_sink_finalize;
339 gobject_klass->set_property = gst_play_sink_set_property;
340 gobject_klass->get_property = gst_play_sink_get_property;
346 * Control the behaviour of playsink.
348 g_object_class_install_property (gobject_klass, PROP_FLAGS,
349 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
350 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
351 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
354 * GstPlaySink:volume:
356 * Get or set the current audio stream volume. 1.0 means 100%,
357 * 0.0 means mute. This uses a linear volume scale.
360 g_object_class_install_property (gobject_klass, PROP_VOLUME,
361 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
362 0.0, VOLUME_MAX_DOUBLE, 1.0,
363 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
364 g_object_class_install_property (gobject_klass, PROP_MUTE,
365 g_param_spec_boolean ("mute", "Mute",
366 "Mute the audio channel without changing the volume", FALSE,
367 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
368 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
369 g_param_spec_string ("subtitle-font-desc",
370 "Subtitle font description",
371 "Pango font description of font "
372 "to be used for subtitle rendering", NULL,
373 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
374 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
375 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
376 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
377 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
378 "be checked for an encoding to use. If that is not set either, "
379 "ISO-8859-15 will be assumed.", NULL,
380 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
381 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
382 g_param_spec_object ("vis-plugin", "Vis plugin",
383 "the visualization element to use (NULL = default)",
384 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
388 * Get the currently rendered or prerolled frame in the video sink.
389 * The #GstCaps on the buffer will describe the format of the buffer.
393 g_object_class_install_property (gobject_klass, PROP_FRAME,
394 gst_param_spec_mini_object ("frame", "Frame",
395 "The last frame (NULL = no video available)",
396 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
398 * GstPlaySink:av-offset:
400 * Control the synchronisation offset between the audio and video streams.
401 * Positive values make the audio ahead of the video and negative values make
402 * the audio go behind the video.
406 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
407 g_param_spec_int64 ("av-offset", "AV Offset",
408 "The synchronisation offset between audio and video in nanoseconds",
409 G_MININT64, G_MAXINT64, 0,
410 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
413 * GstPlaySink:video-sink:
415 * Set the used video sink element. NULL will use the default sink. playsink
416 * must be in %GST_STATE_NULL
420 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
421 g_param_spec_object ("video-sink", "Video Sink",
422 "the video output element to use (NULL = default sink)",
423 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
425 * GstPlaySink:audio-sink:
427 * Set the used audio sink element. NULL will use the default sink. playsink
428 * must be in %GST_STATE_NULL
432 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
433 g_param_spec_object ("audio-sink", "Audio Sink",
434 "the audio output element to use (NULL = default sink)",
435 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
437 * GstPlaySink:text-sink:
439 * Set the used text sink element. NULL will use the default sink. playsink
440 * must be in %GST_STATE_NULL
444 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
445 g_param_spec_object ("text-sink", "Text sink",
446 "the text output element to use (NULL = default textoverlay)",
447 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
450 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
451 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
452 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
455 * GstPlaySink::convert-frame
456 * @playsink: a #GstPlaySink
457 * @caps: the target format of the frame
459 * Action signal to retrieve the currently playing video frame in the format
460 * specified by @caps.
461 * If @caps is %NULL, no conversion will be performed and this function is
462 * equivalent to the #GstPlaySink::frame property.
464 * Returns: a #GstBuffer of the current video frame converted to #caps.
465 * The caps on the buffer will describe the final layout of the buffer data.
466 * %NULL is returned when no current buffer can be retrieved or when the
471 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
472 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
473 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
474 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
476 gst_element_class_add_static_pad_template (gstelement_klass,
478 gst_element_class_add_static_pad_template (gstelement_klass,
480 gst_element_class_add_static_pad_template (gstelement_klass,
482 gst_element_class_add_static_pad_template (gstelement_klass,
484 gst_element_class_add_static_pad_template (gstelement_klass,
486 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
488 "Convenience sink for multiple streams",
489 "Wim Taymans <wim.taymans@gmail.com>");
491 gstelement_klass->change_state =
492 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
493 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
494 gstelement_klass->request_new_pad =
495 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
496 gstelement_klass->release_pad =
497 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
499 gstbin_klass->handle_message =
500 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
502 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
503 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
507 gst_play_sink_init (GstPlaySink * playsink)
510 playsink->video_sink = NULL;
511 playsink->audio_sink = NULL;
512 playsink->visualisation = NULL;
513 playsink->text_sink = NULL;
514 playsink->volume = 1.0;
515 playsink->font_desc = NULL;
516 playsink->subtitle_encoding = NULL;
517 playsink->flags = DEFAULT_FLAGS;
519 playsink->stream_synchronizer =
520 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
521 gst_bin_add (GST_BIN_CAST (playsink),
522 GST_ELEMENT_CAST (playsink->stream_synchronizer));
524 g_static_rec_mutex_init (&playsink->lock);
525 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
529 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
533 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
536 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
542 free_chain (GstPlayChain * chain)
546 gst_object_unref (chain->bin);
552 gst_play_sink_dispose (GObject * object)
554 GstPlaySink *playsink;
556 playsink = GST_PLAY_SINK (object);
558 if (playsink->audio_sink != NULL) {
559 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
560 gst_object_unref (playsink->audio_sink);
561 playsink->audio_sink = NULL;
563 if (playsink->video_sink != NULL) {
564 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
565 gst_object_unref (playsink->video_sink);
566 playsink->video_sink = NULL;
568 if (playsink->visualisation != NULL) {
569 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
570 gst_object_unref (playsink->visualisation);
571 playsink->visualisation = NULL;
573 if (playsink->text_sink != NULL) {
574 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
575 gst_object_unref (playsink->text_sink);
576 playsink->text_sink = NULL;
579 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
580 playsink->videodeinterlacechain = NULL;
581 free_chain ((GstPlayChain *) playsink->videochain);
582 playsink->videochain = NULL;
583 free_chain ((GstPlayChain *) playsink->audiochain);
584 playsink->audiochain = NULL;
585 free_chain ((GstPlayChain *) playsink->vischain);
586 playsink->vischain = NULL;
587 free_chain ((GstPlayChain *) playsink->textchain);
588 playsink->textchain = NULL;
590 if (playsink->audio_tee_sink) {
591 gst_object_unref (playsink->audio_tee_sink);
592 playsink->audio_tee_sink = NULL;
595 if (playsink->audio_tee_vissrc) {
596 gst_element_release_request_pad (playsink->audio_tee,
597 playsink->audio_tee_vissrc);
598 gst_object_unref (playsink->audio_tee_vissrc);
599 playsink->audio_tee_vissrc = NULL;
602 if (playsink->audio_tee_asrc) {
603 gst_element_release_request_pad (playsink->audio_tee,
604 playsink->audio_tee_asrc);
605 gst_object_unref (playsink->audio_tee_asrc);
606 playsink->audio_tee_asrc = NULL;
609 g_free (playsink->font_desc);
610 playsink->font_desc = NULL;
612 g_free (playsink->subtitle_encoding);
613 playsink->subtitle_encoding = NULL;
615 playsink->stream_synchronizer = NULL;
617 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
621 gst_play_sink_finalize (GObject * object)
623 GstPlaySink *playsink;
625 playsink = GST_PLAY_SINK (object);
627 g_static_rec_mutex_free (&playsink->lock);
629 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
633 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
636 GstElement **elem = NULL, *old = NULL;
638 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
640 GST_PLAY_SINK_LOCK (playsink);
642 case GST_PLAY_SINK_TYPE_AUDIO:
643 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
644 elem = &playsink->audio_sink;
646 case GST_PLAY_SINK_TYPE_VIDEO:
647 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
648 elem = &playsink->video_sink;
650 case GST_PLAY_SINK_TYPE_TEXT:
651 elem = &playsink->text_sink;
659 gst_object_ref (sink);
662 GST_PLAY_SINK_UNLOCK (playsink);
666 gst_element_set_state (old, GST_STATE_NULL);
667 gst_object_unref (old);
672 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
674 GstElement *result = NULL;
675 GstElement *elem = NULL, *chainp = NULL;
677 GST_PLAY_SINK_LOCK (playsink);
679 case GST_PLAY_SINK_TYPE_AUDIO:
680 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
682 GstPlayAudioChain *chain;
683 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
684 chainp = chain->sink;
685 elem = playsink->audio_sink;
688 case GST_PLAY_SINK_TYPE_VIDEO:
689 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
691 GstPlayVideoChain *chain;
692 if ((chain = (GstPlayVideoChain *) playsink->videochain))
693 chainp = chain->sink;
694 elem = playsink->video_sink;
697 case GST_PLAY_SINK_TYPE_TEXT:
699 GstPlayTextChain *chain;
700 if ((chain = (GstPlayTextChain *) playsink->textchain))
701 chainp = chain->sink;
702 elem = playsink->text_sink;
709 /* we have an active chain with a sink, get the sink */
710 result = gst_object_ref (chainp);
712 /* nothing found, return last configured sink */
713 if (result == NULL && elem)
714 result = gst_object_ref (elem);
715 GST_PLAY_SINK_UNLOCK (playsink);
721 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
724 GstPlaySink *playsink;
726 playsink = GST_PLAY_SINK (user_data);
727 /* nothing to do here, we need a dummy callback here to make the async call
729 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
733 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
736 GstPlaySink *playsink;
737 GstPlayVisChain *chain;
739 playsink = GST_PLAY_SINK (user_data);
741 GST_PLAY_SINK_LOCK (playsink);
742 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
743 /* now try to change the plugin in the running vis chain */
744 if (!(chain = (GstPlayVisChain *) playsink->vischain))
747 /* unlink the old plugin and unghost the pad */
748 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
749 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
751 /* set the old plugin to NULL and remove */
752 gst_element_set_state (chain->vis, GST_STATE_NULL);
753 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
755 /* add new plugin and set state to playing */
756 chain->vis = playsink->visualisation;
757 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
758 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
761 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
762 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
765 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
766 GST_PAD_LINK_CHECK_NOTHING);
767 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
771 /* Unblock the pad */
772 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
774 GST_PLAY_SINK_UNLOCK (playsink);
778 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
780 GstPlayVisChain *chain;
782 /* setting NULL means creating the default vis plugin */
784 vis = gst_element_factory_make ("goom", "vis");
786 /* simply return if we don't have a vis plugin here */
790 GST_PLAY_SINK_LOCK (playsink);
791 /* first store the new vis */
792 if (playsink->visualisation)
793 gst_object_unref (playsink->visualisation);
795 gst_object_ref_sink (vis);
796 playsink->visualisation = vis;
798 /* now try to change the plugin in the running vis chain, if we have no chain,
799 * we don't bother, any future vis chain will be created with the new vis
801 if (!(chain = (GstPlayVisChain *) playsink->vischain))
804 /* block the pad, the next time the callback is called we can change the
805 * visualisation. It's possible that this never happens or that the pad was
806 * already blocked. If the callback never happens, we don't have new data so
807 * we don't need the new vis plugin. If the pad was already blocked, the
808 * function returns FALSE but the previous pad block will do the right thing
810 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
811 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
814 GST_PLAY_SINK_UNLOCK (playsink);
820 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
822 GstElement *result = NULL;
823 GstPlayVisChain *chain;
825 GST_PLAY_SINK_LOCK (playsink);
826 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
827 /* we have an active chain, get the sink */
829 result = gst_object_ref (chain->vis);
831 /* nothing found, return last configured sink */
832 if (result == NULL && playsink->visualisation)
833 result = gst_object_ref (playsink->visualisation);
834 GST_PLAY_SINK_UNLOCK (playsink);
840 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
842 GstPlayAudioChain *chain;
844 GST_PLAY_SINK_LOCK (playsink);
845 playsink->volume = volume;
846 chain = (GstPlayAudioChain *) playsink->audiochain;
847 if (chain && chain->volume) {
848 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
849 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
850 chain->mute, volume, playsink->mute);
851 /* if there is a mute element or we are not muted, set the volume */
852 if (chain->mute || !playsink->mute)
853 g_object_set (chain->volume, "volume", volume, NULL);
855 GST_LOG_OBJECT (playsink, "no volume element");
856 playsink->volume_changed = TRUE;
858 GST_PLAY_SINK_UNLOCK (playsink);
862 gst_play_sink_get_volume (GstPlaySink * playsink)
865 GstPlayAudioChain *chain;
867 GST_PLAY_SINK_LOCK (playsink);
868 chain = (GstPlayAudioChain *) playsink->audiochain;
869 result = playsink->volume;
870 if (chain && chain->volume) {
871 if (chain->mute || !playsink->mute) {
872 g_object_get (chain->volume, "volume", &result, NULL);
873 playsink->volume = result;
876 GST_PLAY_SINK_UNLOCK (playsink);
882 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
884 GstPlayAudioChain *chain;
886 GST_PLAY_SINK_LOCK (playsink);
887 playsink->mute = mute;
888 chain = (GstPlayAudioChain *) playsink->audiochain;
891 g_object_set (chain->mute, "mute", mute, NULL);
892 } else if (chain->volume) {
894 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
896 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
900 playsink->mute_changed = TRUE;
902 GST_PLAY_SINK_UNLOCK (playsink);
906 gst_play_sink_get_mute (GstPlaySink * playsink)
909 GstPlayAudioChain *chain;
911 GST_PLAY_SINK_LOCK (playsink);
912 chain = (GstPlayAudioChain *) playsink->audiochain;
913 if (chain && chain->mute) {
914 g_object_get (chain->mute, "mute", &result, NULL);
915 playsink->mute = result;
917 result = playsink->mute;
919 GST_PLAY_SINK_UNLOCK (playsink);
925 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
929 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
930 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
934 add_chain (GstPlayChain * chain, gboolean add)
936 if (chain->added == add)
940 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
942 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
943 /* we don't want to lose our sink status */
944 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
953 activate_chain (GstPlayChain * chain, gboolean activate)
957 if (chain->activated == activate)
960 GST_OBJECT_LOCK (chain->playsink);
961 state = GST_STATE_TARGET (chain->playsink);
962 GST_OBJECT_UNLOCK (chain->playsink);
965 gst_element_set_state (chain->bin, state);
967 gst_element_set_state (chain->bin, GST_STATE_NULL);
969 chain->activated = activate;
975 element_is_sink (GstElement * element)
979 GST_OBJECT_LOCK (element);
980 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
981 GST_OBJECT_UNLOCK (element);
983 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
988 element_has_property (GstElement * element, const gchar * pname, GType type)
992 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
995 GST_DEBUG_OBJECT (element, "no %s property", pname);
999 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1000 g_type_is_a (pspec->value_type, type)) {
1001 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1002 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1006 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1007 "and we expected it to be of type %s", pname,
1008 g_type_name (pspec->value_type), g_type_name (type));
1015 const gchar *prop_name;
1018 } FindPropertyHelper;
1021 find_property (GstElement * element, FindPropertyHelper * helper)
1023 if (helper->need_sink && !element_is_sink (element)) {
1024 gst_object_unref (element);
1028 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1029 gst_object_unref (element);
1033 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1034 (helper->need_sink) ? "sink" : "element");
1035 return 0; /* keep it */
1038 /* FIXME: why not move these functions into core? */
1039 /* find a sink in the hierarchy with a property named @name. This function does
1040 * not increase the refcount of the returned object and thus remains valid as
1041 * long as the bin is valid. */
1043 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1044 const gchar * name, GType expected_type)
1046 GstElement *result = NULL;
1049 if (element_has_property (obj, name, expected_type)) {
1051 } else if (GST_IS_BIN (obj)) {
1052 FindPropertyHelper helper = { name, expected_type, TRUE };
1054 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1055 result = gst_iterator_find_custom (it,
1056 (GCompareFunc) find_property, &helper);
1057 gst_iterator_free (it);
1058 /* we don't need the extra ref */
1060 gst_object_unref (result);
1065 /* find an object in the hierarchy with a property named @name */
1067 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1068 const gchar * name, GType expected_type)
1070 GstElement *result = NULL;
1073 if (GST_IS_BIN (obj)) {
1074 FindPropertyHelper helper = { name, expected_type, FALSE };
1076 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1077 result = gst_iterator_find_custom (it,
1078 (GCompareFunc) find_property, &helper);
1079 gst_iterator_free (it);
1081 if (element_has_property (obj, name, expected_type)) {
1083 gst_object_ref (obj);
1090 do_async_start (GstPlaySink * playsink)
1092 GstMessage *message;
1094 if (!playsink->need_async_start) {
1095 GST_INFO_OBJECT (playsink, "no async_start needed");
1099 playsink->async_pending = TRUE;
1101 GST_INFO_OBJECT (playsink, "Sending async_start message");
1102 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1103 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1104 (playsink), message);
1108 do_async_done (GstPlaySink * playsink)
1110 GstMessage *message;
1112 if (playsink->async_pending) {
1113 GST_INFO_OBJECT (playsink, "Sending async_done message");
1114 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1115 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1116 (playsink), message);
1118 playsink->async_pending = FALSE;
1121 playsink->need_async_start = FALSE;
1124 /* try to change the state of an element. This function returns the element when
1125 * the state change could be performed. When this function returns NULL an error
1126 * occured and the element is unreffed if @unref is TRUE. */
1128 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1130 GstStateChangeReturn ret;
1133 ret = gst_element_set_state (element, GST_STATE_READY);
1134 if (ret == GST_STATE_CHANGE_FAILURE) {
1135 GST_DEBUG_OBJECT (playsink, "failed state change..");
1136 gst_element_set_state (element, GST_STATE_NULL);
1138 gst_object_unref (element);
1145 /* make the element (bin) that contains the elements needed to perform
1146 * video display. Only used for *raw* video streams.
1148 * +------------------------------------------------------------+
1150 * | +-------+ +----------+ +----------+ +---------+ |
1151 * | | queue | |colorspace| |videoscale| |videosink| |
1152 * | +-sink src-sink src-sink src-sink | |
1153 * | | +-------+ +----------+ +----------+ +---------+ |
1155 * +------------------------------------------------------------+
1158 static GstPlayVideoDeinterlaceChain *
1159 gen_video_deinterlace_chain (GstPlaySink * playsink)
1161 GstPlayVideoDeinterlaceChain *chain;
1164 GstElement *head = NULL, *prev = NULL;
1166 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1167 chain->chain.playsink = playsink;
1169 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1171 /* create a bin to hold objects, as we create them we add them to this bin so
1172 * that when something goes wrong we only need to unref the bin */
1173 chain->chain.bin = gst_bin_new ("vdbin");
1174 bin = GST_BIN_CAST (chain->chain.bin);
1175 gst_object_ref_sink (bin);
1177 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1178 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1179 if (chain->conv == NULL) {
1180 post_missing_element_message (playsink, COLORSPACE);
1181 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1182 (_("Missing element '%s' - check your GStreamer installation."),
1183 COLORSPACE), ("video rendering might fail"));
1185 gst_bin_add (bin, chain->conv);
1190 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1191 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1192 if (chain->deinterlace == NULL) {
1193 post_missing_element_message (playsink, "deinterlace");
1194 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1195 (_("Missing element '%s' - check your GStreamer installation."),
1196 "deinterlace"), ("deinterlacing won't work"));
1198 gst_bin_add (bin, chain->deinterlace);
1200 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1201 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1204 head = chain->deinterlace;
1206 prev = chain->deinterlace;
1210 pad = gst_element_get_static_pad (head, "sink");
1211 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1212 gst_object_unref (pad);
1214 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1218 pad = gst_element_get_static_pad (prev, "src");
1219 chain->srcpad = gst_ghost_pad_new ("src", pad);
1220 gst_object_unref (pad);
1222 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1225 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1226 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1232 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1233 (NULL), ("Failed to configure the video deinterlace chain."));
1234 free_chain ((GstPlayChain *) chain);
1239 /* make the element (bin) that contains the elements needed to perform
1242 * +------------------------------------------------------------+
1244 * | +-------+ +----------+ +----------+ +---------+ |
1245 * | | queue | |colorspace| |videoscale| |videosink| |
1246 * | +-sink src-sink src-sink src-sink | |
1247 * | | +-------+ +----------+ +----------+ +---------+ |
1249 * +------------------------------------------------------------+
1252 static GstPlayVideoChain *
1253 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1255 GstPlayVideoChain *chain;
1258 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1260 chain = g_new0 (GstPlayVideoChain, 1);
1261 chain->chain.playsink = playsink;
1262 chain->chain.raw = raw;
1264 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1266 if (playsink->video_sink) {
1267 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1268 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1270 /* only try fallback if no specific sink was chosen */
1271 if (chain->sink == NULL) {
1272 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1273 elem = gst_element_factory_make ("autovideosink", "videosink");
1274 chain->sink = try_element (playsink, elem, TRUE);
1276 if (chain->sink == NULL) {
1277 /* if default sink from config.h is different then try it too */
1278 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1279 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1280 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1281 chain->sink = try_element (playsink, elem, TRUE);
1285 playsink->video_sink = gst_object_ref (chain->sink);
1287 if (chain->sink == NULL)
1291 /* if we can disable async behaviour of the sink, we can avoid adding a
1292 * queue for the audio chain. */
1294 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1297 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1298 async, GST_ELEMENT_NAME (elem));
1299 g_object_set (elem, "async", async, NULL);
1300 chain->async = async;
1302 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1303 chain->async = TRUE;
1306 /* find ts-offset element */
1307 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1308 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1311 /* create a bin to hold objects, as we create them we add them to this bin so
1312 * that when something goes wrong we only need to unref the bin */
1313 chain->chain.bin = gst_bin_new ("vbin");
1314 bin = GST_BIN_CAST (chain->chain.bin);
1315 gst_object_ref_sink (bin);
1316 gst_bin_add (bin, chain->sink);
1318 /* decouple decoder from sink, this improves playback quite a lot since the
1319 * decoder can continue while the sink blocks for synchronisation. We don't
1320 * need a lot of buffers as this consumes a lot of memory and we don't want
1321 * too little because else we would be context switching too quickly. */
1322 chain->queue = gst_element_factory_make ("queue", "vqueue");
1323 if (chain->queue == NULL) {
1324 post_missing_element_message (playsink, "queue");
1325 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1326 (_("Missing element '%s' - check your GStreamer installation."),
1327 "queue"), ("video rendering might be suboptimal"));
1331 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1332 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1333 gst_bin_add (bin, chain->queue);
1334 head = prev = chain->queue;
1337 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1338 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1340 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1341 gst_bin_add (bin, chain->conv);
1343 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1344 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1353 GST_DEBUG_OBJECT (playsink, "linking to sink");
1354 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1355 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1359 pad = gst_element_get_static_pad (head, "sink");
1360 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1361 gst_object_unref (pad);
1363 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1370 if (!elem && !playsink->video_sink) {
1371 post_missing_element_message (playsink, "autovideosink");
1372 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1373 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1374 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1375 (_("Both autovideosink and %s elements are missing."),
1376 DEFAULT_VIDEOSINK), (NULL));
1378 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1379 (_("The autovideosink element is missing.")), (NULL));
1382 if (playsink->video_sink) {
1383 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1384 (_("Configured videosink %s is not working."),
1385 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1386 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1387 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1388 (_("Both autovideosink and %s elements are not working."),
1389 DEFAULT_VIDEOSINK), (NULL));
1391 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1392 (_("The autovideosink element is not working.")), (NULL));
1395 free_chain ((GstPlayChain *) chain);
1400 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1401 (NULL), ("Failed to configure the video sink."));
1402 /* checking sink made it READY */
1403 gst_element_set_state (chain->sink, GST_STATE_NULL);
1404 /* Remove chain from the bin to allow reuse later */
1405 gst_bin_remove (bin, chain->sink);
1406 free_chain ((GstPlayChain *) chain);
1412 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1415 GstPlayVideoChain *chain;
1416 GstStateChangeReturn ret;
1418 chain = playsink->videochain;
1420 chain->chain.raw = raw;
1422 /* if the chain was active we don't do anything */
1423 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1426 /* try to set the sink element to READY again */
1427 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1428 if (ret == GST_STATE_CHANGE_FAILURE)
1431 /* find ts-offset element */
1433 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1434 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1437 /* if we can disable async behaviour of the sink, we can avoid adding a
1438 * queue for the audio chain. */
1440 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1443 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1444 async, GST_ELEMENT_NAME (elem));
1445 g_object_set (elem, "async", async, NULL);
1446 chain->async = async;
1448 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1449 chain->async = TRUE;
1454 /* make an element for playback of video with subtitles embedded.
1455 * Only used for *raw* video streams.
1457 * +--------------------------------------------+
1459 * | +--------+ +-----------------+ |
1460 * | | queue | | subtitleoverlay | |
1461 * video--src sink---video_sink | |
1462 * | +--------+ | src--src
1463 * text------------------text_sink | |
1464 * | +-----------------+ |
1465 * +--------------------------------------------+
1468 static GstPlayTextChain *
1469 gen_text_chain (GstPlaySink * playsink)
1471 GstPlayTextChain *chain;
1474 GstPad *videosinkpad, *textsinkpad, *srcpad;
1476 chain = g_new0 (GstPlayTextChain, 1);
1477 chain->chain.playsink = playsink;
1479 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1481 chain->chain.bin = gst_bin_new ("tbin");
1482 bin = GST_BIN_CAST (chain->chain.bin);
1483 gst_object_ref_sink (bin);
1485 videosinkpad = textsinkpad = srcpad = NULL;
1487 /* first try to hook the text pad to the custom sink */
1488 if (playsink->text_sink) {
1489 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1490 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1493 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1496 /* make sure the sparse subtitles don't participate in the preroll */
1497 g_object_set (elem, "async", FALSE, NULL);
1498 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1499 gst_bin_add (bin, chain->sink);
1500 /* NOTE streamsynchronizer needs streams decoupled */
1501 /* make a little queue */
1502 chain->queue = gst_element_factory_make ("queue", "subqueue");
1503 if (chain->queue == NULL) {
1504 post_missing_element_message (playsink, "queue");
1505 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1506 (_("Missing element '%s' - check your GStreamer installation."),
1507 "queue"), ("rendering might be suboptimal"));
1509 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1510 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1511 "silent", TRUE, NULL);
1512 gst_bin_add (bin, chain->queue);
1514 /* we have a custom sink, this will be our textsinkpad */
1515 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1516 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1517 /* we're all fine now and we can add the sink to the chain */
1518 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1519 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1521 GST_WARNING_OBJECT (playsink,
1522 "can't find a sink pad on custom text sink");
1523 gst_bin_remove (bin, chain->sink);
1524 gst_bin_remove (bin, chain->queue);
1526 chain->queue = NULL;
1528 /* try to set sync to true but it's no biggie when we can't */
1530 gst_play_sink_find_property_sinks (playsink, chain->sink,
1531 "sync", G_TYPE_BOOLEAN)))
1532 g_object_set (elem, "sync", TRUE, NULL);
1535 gst_bin_remove (bin, chain->sink);
1537 GST_WARNING_OBJECT (playsink,
1538 "can't find async property in custom text sink");
1541 if (textsinkpad == NULL) {
1542 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1543 (_("Custom text sink element is not usable.")),
1544 ("fallback to default textoverlay"));
1548 if (textsinkpad == NULL) {
1549 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1550 /* make a little queue */
1551 chain->queue = gst_element_factory_make ("queue", "vqueue");
1552 if (chain->queue == NULL) {
1553 post_missing_element_message (playsink, "queue");
1554 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1555 (_("Missing element '%s' - check your GStreamer installation."),
1556 "queue"), ("video rendering might be suboptimal"));
1558 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1559 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1560 "silent", TRUE, NULL);
1561 gst_bin_add (bin, chain->queue);
1562 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1566 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1567 if (chain->overlay == NULL) {
1568 post_missing_element_message (playsink, "subtitleoverlay");
1569 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1570 (_("Missing element '%s' - check your GStreamer installation."),
1571 "subtitleoverlay"), ("subtitle rendering disabled"));
1573 GstElement *element;
1575 gst_bin_add (bin, chain->overlay);
1577 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1578 if (playsink->font_desc) {
1579 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1580 playsink->font_desc, NULL);
1582 if (playsink->subtitle_encoding) {
1583 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1584 playsink->subtitle_encoding, NULL);
1587 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1588 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1590 /* make another little queue to decouple streams */
1591 element = gst_element_factory_make ("queue", "subqueue");
1592 if (element == NULL) {
1593 post_missing_element_message (playsink, "queue");
1594 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1595 (_("Missing element '%s' - check your GStreamer installation."),
1596 "queue"), ("rendering might be suboptimal"));
1598 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1599 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1600 "silent", TRUE, NULL);
1601 gst_bin_add (bin, element);
1602 gst_element_link_pads_full (element, "src", chain->overlay,
1603 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1604 textsinkpad = gst_element_get_static_pad (element, "sink");
1605 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1611 if (videosinkpad == NULL) {
1612 /* if we still don't have a videosink, we don't have an overlay. the only
1613 * thing we can do is insert an identity and ghost the src
1615 chain->identity = gst_element_factory_make ("identity", "tidentity");
1616 if (chain->identity == NULL) {
1617 post_missing_element_message (playsink, "identity");
1618 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1619 (_("Missing element '%s' - check your GStreamer installation."),
1620 "identity"), (NULL));
1622 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1623 g_object_set (chain->identity, "silent", TRUE, NULL);
1624 gst_bin_add (bin, chain->identity);
1625 srcpad = gst_element_get_static_pad (chain->identity, "src");
1626 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1630 /* expose the ghostpads */
1632 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1633 gst_object_unref (videosinkpad);
1634 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1637 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1638 gst_object_unref (textsinkpad);
1639 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1642 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1643 gst_object_unref (srcpad);
1644 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1651 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1655 g_object_get (object, "volume", &vol, NULL);
1656 playsink->volume = vol;
1658 g_object_notify (G_OBJECT (playsink), "volume");
1662 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1666 g_object_get (object, "mute", &mute, NULL);
1667 playsink->mute = mute;
1669 g_object_notify (G_OBJECT (playsink), "mute");
1672 /* make the chain that contains the elements needed to perform
1675 * We add a tee as the first element so that we can link the visualisation chain
1676 * to it when requested.
1678 * +-------------------------------------------------------------+
1680 * | +---------+ +----------+ +---------+ +---------+ |
1681 * | |audioconv| |audioscale| | volume | |audiosink| |
1682 * | +-srck src-sink src-sink src-sink | |
1683 * | | +---------+ +----------+ +---------+ +---------+ |
1685 * +-------------------------------------------------------------+
1687 static GstPlayAudioChain *
1688 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1690 GstPlayAudioChain *chain;
1692 gboolean have_volume;
1694 GstElement *head, *prev, *elem = NULL;
1696 chain = g_new0 (GstPlayAudioChain, 1);
1697 chain->chain.playsink = playsink;
1698 chain->chain.raw = raw;
1700 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1702 if (playsink->audio_sink) {
1703 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1704 playsink->audio_sink);
1705 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1707 /* only try fallback if no specific sink was chosen */
1708 if (chain->sink == NULL) {
1709 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1710 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1711 chain->sink = try_element (playsink, elem, TRUE);
1713 if (chain->sink == NULL) {
1714 /* if default sink from config.h is different then try it too */
1715 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1716 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1717 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1718 chain->sink = try_element (playsink, elem, TRUE);
1722 playsink->audio_sink = gst_object_ref (chain->sink);
1724 if (chain->sink == NULL)
1727 chain->chain.bin = gst_bin_new ("abin");
1728 bin = GST_BIN_CAST (chain->chain.bin);
1729 gst_object_ref_sink (bin);
1730 gst_bin_add (bin, chain->sink);
1732 /* we have to add a queue when we need to decouple for the video sink in
1733 * visualisations and for streamsynchronizer */
1734 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1735 chain->queue = gst_element_factory_make ("queue", "aqueue");
1736 if (chain->queue == NULL) {
1737 post_missing_element_message (playsink, "queue");
1738 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1739 (_("Missing element '%s' - check your GStreamer installation."),
1740 "queue"), ("audio playback and visualizations might not work"));
1744 g_object_set (chain->queue, "silent", TRUE, NULL);
1745 gst_bin_add (bin, chain->queue);
1746 prev = head = chain->queue;
1749 /* find ts-offset element */
1750 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1751 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1754 /* check if the sink, or something within the sink, has the volume property.
1755 * If it does we don't need to add a volume element. */
1757 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1760 chain->volume = elem;
1762 g_signal_connect (chain->volume, "notify::volume",
1763 G_CALLBACK (notify_volume_cb), playsink);
1765 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1767 chain->sink_volume = TRUE;
1768 /* if the sink also has a mute property we can use this as well. We'll only
1769 * use the mute property if there is a volume property. We can simulate the
1770 * mute with the volume otherwise. */
1772 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1775 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1776 g_signal_connect (chain->mute, "notify::mute",
1777 G_CALLBACK (notify_mute_cb), playsink);
1779 /* use the sink to control the volume and mute */
1780 if (playsink->volume_changed) {
1781 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1782 playsink->volume_changed = FALSE;
1784 if (playsink->mute_changed) {
1786 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1789 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1791 playsink->mute_changed = FALSE;
1794 /* no volume, we need to add a volume element when we can */
1795 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1796 have_volume = FALSE;
1797 chain->sink_volume = FALSE;
1800 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1801 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1802 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1803 gboolean use_volume =
1804 !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME;
1805 GST_DEBUG_OBJECT (playsink,
1806 "creating audioconvert with use-converters %d, use-volume %d",
1807 use_converters, use_volume);
1809 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1810 "use-converters", use_converters, "use-volume", use_volume, NULL);
1811 gst_bin_add (bin, chain->conv);
1813 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1814 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1821 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1822 GstPlaySinkAudioConvert *conv =
1823 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1826 chain->volume = conv->volume;
1829 g_signal_connect (chain->volume, "notify::volume",
1830 G_CALLBACK (notify_volume_cb), playsink);
1832 /* volume also has the mute property */
1833 chain->mute = chain->volume;
1834 g_signal_connect (chain->mute, "notify::mute",
1835 G_CALLBACK (notify_mute_cb), playsink);
1837 /* configure with the latest volume and mute */
1838 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1840 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1846 /* we only have to link to the previous element if we have something in
1847 * front of the sink */
1848 GST_DEBUG_OBJECT (playsink, "linking to sink");
1849 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1850 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1854 /* post a warning if we have no way to configure the volume */
1856 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1857 (_("No volume control found")), ("Volume/mute is not available"));
1860 /* and ghost the sinkpad of the headmost element */
1861 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1862 pad = gst_element_get_static_pad (head, "sink");
1863 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1864 gst_object_unref (pad);
1865 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1872 if (!elem && !playsink->audio_sink) {
1873 post_missing_element_message (playsink, "autoaudiosink");
1874 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1875 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1876 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1877 (_("Both autoaudiosink and %s elements are missing."),
1878 DEFAULT_AUDIOSINK), (NULL));
1880 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1881 (_("The autoaudiosink element is missing.")), (NULL));
1884 if (playsink->audio_sink) {
1885 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1886 (_("Configured audiosink %s is not working."),
1887 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1888 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1889 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1890 (_("Both autoaudiosink and %s elements are not working."),
1891 DEFAULT_AUDIOSINK), (NULL));
1893 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1894 (_("The autoaudiosink element is not working.")), (NULL));
1897 free_chain ((GstPlayChain *) chain);
1902 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1903 (NULL), ("Failed to configure the audio sink."));
1904 /* checking sink made it READY */
1905 gst_element_set_state (chain->sink, GST_STATE_NULL);
1906 /* Remove chain from the bin to allow reuse later */
1907 gst_bin_remove (bin, chain->sink);
1908 free_chain ((GstPlayChain *) chain);
1914 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1917 GstPlayAudioChain *chain;
1918 GstStateChangeReturn ret;
1920 chain = playsink->audiochain;
1922 chain->chain.raw = raw;
1924 /* if the chain was active we don't do anything */
1925 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1928 /* try to set the sink element to READY again */
1929 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1930 if (ret == GST_STATE_CHANGE_FAILURE)
1933 /* find ts-offset element */
1934 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1935 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1938 /* check if the sink, or something within the sink, has the volume property.
1939 * If it does we don't need to add a volume element. */
1941 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1944 chain->volume = elem;
1946 if (playsink->volume_changed) {
1947 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1949 /* use the sink to control the volume */
1950 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1951 playsink->volume_changed = FALSE;
1954 g_signal_connect (chain->volume, "notify::volume",
1955 G_CALLBACK (notify_volume_cb), playsink);
1956 /* if the sink also has a mute property we can use this as well. We'll only
1957 * use the mute property if there is a volume property. We can simulate the
1958 * mute with the volume otherwise. */
1960 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1963 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1964 g_signal_connect (chain->mute, "notify::mute",
1965 G_CALLBACK (notify_mute_cb), playsink);
1968 g_object_set (chain->conv, "use-volume", FALSE, NULL);
1970 GstPlaySinkAudioConvert *conv =
1971 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1973 /* no volume, we need to add a volume element when we can */
1974 g_object_set (chain->conv, "use-volume", TRUE, NULL);
1975 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1977 /* Disconnect signals */
1978 disconnect_chain (chain, playsink);
1981 chain->volume = conv->volume;
1982 chain->mute = chain->volume;
1984 g_signal_connect (chain->volume, "notify::volume",
1985 G_CALLBACK (notify_volume_cb), playsink);
1987 g_signal_connect (chain->mute, "notify::mute",
1988 G_CALLBACK (notify_mute_cb), playsink);
1990 /* configure with the latest volume and mute */
1991 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1992 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1995 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2001 * +-------------------------------------------------------------------+
2003 * | +----------+ +------------+ +----------+ +-------+ |
2004 * | | visqueue | | audioconv | | audiores | | vis | |
2005 * | +-sink src-sink + samp src-sink src-sink src-+ |
2006 * | | +----------+ +------------+ +----------+ +-------+ | |
2008 * +-------------------------------------------------------------------+
2011 static GstPlayVisChain *
2012 gen_vis_chain (GstPlaySink * playsink)
2014 GstPlayVisChain *chain;
2020 chain = g_new0 (GstPlayVisChain, 1);
2021 chain->chain.playsink = playsink;
2023 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2025 chain->chain.bin = gst_bin_new ("visbin");
2026 bin = GST_BIN_CAST (chain->chain.bin);
2027 gst_object_ref_sink (bin);
2029 /* we're queuing raw audio here, we can remove this queue when we can disable
2030 * async behaviour in the video sink. */
2031 chain->queue = gst_element_factory_make ("queue", "visqueue");
2032 if (chain->queue == NULL)
2034 g_object_set (chain->queue, "silent", TRUE, NULL);
2035 gst_bin_add (bin, chain->queue);
2037 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2038 if (chain->conv == NULL)
2039 goto no_audioconvert;
2040 gst_bin_add (bin, chain->conv);
2042 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2043 if (chain->resample == NULL)
2044 goto no_audioresample;
2045 gst_bin_add (bin, chain->resample);
2047 /* this pad will be used for blocking the dataflow and switching the vis
2049 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2051 if (playsink->visualisation) {
2052 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2053 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2055 if (chain->vis == NULL) {
2056 GST_DEBUG_OBJECT (playsink, "trying goom");
2057 elem = gst_element_factory_make ("goom", "vis");
2058 chain->vis = try_element (playsink, elem, TRUE);
2060 if (chain->vis == NULL)
2063 gst_bin_add (bin, chain->vis);
2065 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2066 GST_PAD_LINK_CHECK_NOTHING);
2068 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2069 GST_PAD_LINK_CHECK_NOTHING);
2071 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2072 GST_PAD_LINK_CHECK_NOTHING);
2076 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2077 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2079 pad = gst_element_get_static_pad (chain->queue, "sink");
2080 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2081 gst_object_unref (pad);
2082 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2084 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2085 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2092 post_missing_element_message (playsink, "queue");
2093 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2094 (_("Missing element '%s' - check your GStreamer installation."),
2096 free_chain ((GstPlayChain *) chain);
2101 post_missing_element_message (playsink, "audioconvert");
2102 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2103 (_("Missing element '%s' - check your GStreamer installation."),
2104 "audioconvert"), ("possibly a liboil version mismatch?"));
2105 free_chain ((GstPlayChain *) chain);
2110 post_missing_element_message (playsink, "audioresample");
2111 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2112 (_("Missing element '%s' - check your GStreamer installation."),
2113 "audioresample"), (NULL));
2114 free_chain ((GstPlayChain *) chain);
2119 post_missing_element_message (playsink, "goom");
2120 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2121 (_("Missing element '%s' - check your GStreamer installation."),
2123 free_chain ((GstPlayChain *) chain);
2128 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2129 (NULL), ("Failed to configure the visualisation element."));
2130 /* element made it to READY */
2131 gst_element_set_state (chain->vis, GST_STATE_NULL);
2132 free_chain ((GstPlayChain *) chain);
2137 /* this function is called when all the request pads are requested and when we
2138 * have to construct the final pipeline. Based on the flags we construct the
2139 * final output pipelines.
2142 gst_play_sink_reconfigure (GstPlaySink * playsink)
2145 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2147 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2149 /* assume we need nothing */
2150 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2152 GST_PLAY_SINK_LOCK (playsink);
2153 GST_OBJECT_LOCK (playsink);
2154 /* get flags, there are protected with the object lock */
2155 flags = playsink->flags;
2156 GST_OBJECT_UNLOCK (playsink);
2158 /* figure out which components we need */
2159 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2160 /* we have subtitles and we are requested to show it */
2164 if (((flags & GST_PLAY_FLAG_VIDEO)
2165 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2166 /* we have video and we are requested to show it */
2169 /* we only deinterlace if native video is not requested and
2170 * we have raw video */
2171 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2172 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2173 need_deinterlace = TRUE;
2176 if (playsink->audio_pad) {
2177 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2180 if (playsink->audio_pad_raw) {
2181 /* only can do vis with raw uncompressed audio */
2182 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2183 /* also add video when we add visualisation */
2190 /* we have a text_pad and we need text rendering, in this case we need a
2191 * video_pad to combine the video with the text or visualizations */
2192 if (need_text && !need_video) {
2193 if (playsink->video_pad) {
2195 } else if (need_audio) {
2196 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2197 (_("Can't play a text file without video or visualizations.")),
2198 ("Have text pad but no video pad or visualizations"));
2201 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2202 (_("Can't play a text file without video or visualizations.")),
2203 ("Have text pad but no video pad or visualizations"));
2204 GST_PLAY_SINK_UNLOCK (playsink);
2209 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2210 need_video, need_vis, need_text);
2212 /* set up video pipeline */
2214 gboolean raw, async;
2216 /* we need a raw sink when we do vis or when we have a raw pad */
2217 raw = need_vis ? TRUE : playsink->video_pad_raw;
2218 /* we try to set the sink async=FALSE when we need vis, this way we can
2219 * avoid a queue in the audio chain. */
2222 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2223 playsink->video_pad_raw);
2225 if (playsink->videochain) {
2226 /* try to reactivate the chain */
2227 if (!setup_video_chain (playsink, raw, async)) {
2228 if (playsink->video_sinkpad_stream_synchronizer) {
2229 gst_element_release_request_pad (GST_ELEMENT_CAST
2230 (playsink->stream_synchronizer),
2231 playsink->video_sinkpad_stream_synchronizer);
2232 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2233 playsink->video_sinkpad_stream_synchronizer = NULL;
2234 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2235 playsink->video_srcpad_stream_synchronizer = NULL;
2238 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2240 /* Remove the sink from the bin to keep its state
2241 * and unparent it to allow reuse */
2242 if (playsink->videochain->sink)
2243 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2244 playsink->videochain->sink);
2246 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2247 free_chain ((GstPlayChain *) playsink->videochain);
2248 playsink->videochain = NULL;
2252 if (!playsink->videochain)
2253 playsink->videochain = gen_video_chain (playsink, raw, async);
2254 if (!playsink->videochain)
2257 if (!playsink->video_sinkpad_stream_synchronizer) {
2260 playsink->video_sinkpad_stream_synchronizer =
2261 gst_element_get_request_pad (GST_ELEMENT_CAST
2262 (playsink->stream_synchronizer), "sink_%d");
2263 it = gst_pad_iterate_internal_links
2264 (playsink->video_sinkpad_stream_synchronizer);
2266 gst_iterator_next (it,
2267 (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2268 g_assert (playsink->video_srcpad_stream_synchronizer);
2269 gst_iterator_free (it);
2272 if (playsink->video_pad)
2273 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2274 playsink->video_sinkpad_stream_synchronizer);
2276 if (need_deinterlace) {
2277 if (!playsink->videodeinterlacechain)
2278 playsink->videodeinterlacechain =
2279 gen_video_deinterlace_chain (playsink);
2280 if (!playsink->videodeinterlacechain)
2283 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2285 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2287 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2288 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2290 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2291 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2293 if (playsink->videodeinterlacechain) {
2294 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2295 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2300 GST_DEBUG_OBJECT (playsink, "adding video chain");
2301 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2302 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2303 /* if we are not part of vis or subtitles, set the ghostpad target */
2304 if (!need_vis && !need_text && (!playsink->textchain
2305 || !playsink->text_pad)) {
2306 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2307 if (need_deinterlace)
2308 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2309 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2311 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2312 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2315 GST_DEBUG_OBJECT (playsink, "no video needed");
2316 if (playsink->videochain) {
2317 GST_DEBUG_OBJECT (playsink, "removing video chain");
2318 if (playsink->vischain) {
2321 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2323 /* also had visualisation, release the tee srcpad before we then
2324 * unlink the video from it */
2325 if (playsink->audio_tee_vissrc) {
2326 gst_element_release_request_pad (playsink->audio_tee,
2327 playsink->audio_tee_vissrc);
2328 gst_object_unref (playsink->audio_tee_vissrc);
2329 playsink->audio_tee_vissrc = NULL;
2332 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2333 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2336 if (playsink->video_sinkpad_stream_synchronizer) {
2337 gst_element_release_request_pad (GST_ELEMENT_CAST
2338 (playsink->stream_synchronizer),
2339 playsink->video_sinkpad_stream_synchronizer);
2340 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2341 playsink->video_sinkpad_stream_synchronizer = NULL;
2342 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2343 playsink->video_srcpad_stream_synchronizer = NULL;
2346 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2347 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2348 if (playsink->videochain->ts_offset)
2349 gst_object_unref (playsink->videochain->ts_offset);
2350 playsink->videochain->ts_offset = NULL;
2353 if (playsink->videodeinterlacechain) {
2354 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2355 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2358 if (playsink->video_pad)
2359 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2365 GST_DEBUG_OBJECT (playsink, "adding audio");
2367 /* get a raw sink if we are asked for a raw pad */
2368 raw = playsink->audio_pad_raw;
2370 if (playsink->audiochain) {
2371 /* try to reactivate the chain */
2372 if (!setup_audio_chain (playsink, raw)) {
2373 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2374 if (playsink->audio_tee_asrc) {
2375 gst_element_release_request_pad (playsink->audio_tee,
2376 playsink->audio_tee_asrc);
2377 gst_object_unref (playsink->audio_tee_asrc);
2378 playsink->audio_tee_asrc = NULL;
2381 if (playsink->audio_sinkpad_stream_synchronizer) {
2382 gst_element_release_request_pad (GST_ELEMENT_CAST
2383 (playsink->stream_synchronizer),
2384 playsink->audio_sinkpad_stream_synchronizer);
2385 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2386 playsink->audio_sinkpad_stream_synchronizer = NULL;
2387 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2388 playsink->audio_srcpad_stream_synchronizer = NULL;
2391 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2393 /* Remove the sink from the bin to keep its state
2394 * and unparent it to allow reuse */
2395 if (playsink->audiochain->sink)
2396 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2397 playsink->audiochain->sink);
2399 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2400 disconnect_chain (playsink->audiochain, playsink);
2401 playsink->audiochain->volume = NULL;
2402 playsink->audiochain->mute = NULL;
2403 if (playsink->audiochain->ts_offset)
2404 gst_object_unref (playsink->audiochain->ts_offset);
2405 playsink->audiochain->ts_offset = NULL;
2406 free_chain ((GstPlayChain *) playsink->audiochain);
2407 playsink->audiochain = NULL;
2408 playsink->volume_changed = playsink->mute_changed = FALSE;
2412 if (!playsink->audiochain) {
2413 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2414 playsink->audiochain = gen_audio_chain (playsink, raw);
2417 if (!playsink->audio_sinkpad_stream_synchronizer) {
2420 playsink->audio_sinkpad_stream_synchronizer =
2421 gst_element_get_request_pad (GST_ELEMENT_CAST
2422 (playsink->stream_synchronizer), "sink_%d");
2423 it = gst_pad_iterate_internal_links
2424 (playsink->audio_sinkpad_stream_synchronizer);
2426 gst_iterator_next (it,
2427 (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2428 g_assert (playsink->audio_srcpad_stream_synchronizer);
2429 gst_iterator_free (it);
2432 if (playsink->audiochain) {
2433 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2434 if (playsink->audio_tee_asrc == NULL) {
2435 playsink->audio_tee_asrc =
2436 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2438 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2439 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2440 gst_pad_link_full (playsink->audio_tee_asrc,
2441 playsink->audio_sinkpad_stream_synchronizer,
2442 GST_PAD_LINK_CHECK_NOTHING);
2443 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2444 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2447 GST_DEBUG_OBJECT (playsink, "no audio needed");
2448 /* we have no audio or we are requested to not play audio */
2449 if (playsink->audiochain) {
2450 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2451 /* release the audio pad */
2452 if (playsink->audio_tee_asrc) {
2453 gst_element_release_request_pad (playsink->audio_tee,
2454 playsink->audio_tee_asrc);
2455 gst_object_unref (playsink->audio_tee_asrc);
2456 playsink->audio_tee_asrc = NULL;
2459 if (playsink->audio_sinkpad_stream_synchronizer) {
2460 gst_element_release_request_pad (GST_ELEMENT_CAST
2461 (playsink->stream_synchronizer),
2462 playsink->audio_sinkpad_stream_synchronizer);
2463 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2464 playsink->audio_sinkpad_stream_synchronizer = NULL;
2465 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2466 playsink->audio_srcpad_stream_synchronizer = NULL;
2469 if (playsink->audiochain->sink_volume) {
2470 disconnect_chain (playsink->audiochain, playsink);
2471 playsink->audiochain->volume = NULL;
2472 playsink->audiochain->mute = NULL;
2473 if (playsink->audiochain->ts_offset)
2474 gst_object_unref (playsink->audiochain->ts_offset);
2475 playsink->audiochain->ts_offset = NULL;
2477 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2478 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2485 if (!playsink->vischain)
2486 playsink->vischain = gen_vis_chain (playsink);
2488 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2490 if (playsink->vischain) {
2491 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2493 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2494 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2495 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2496 if (playsink->audio_tee_vissrc == NULL) {
2497 playsink->audio_tee_vissrc =
2498 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2500 gst_pad_link_full (playsink->audio_tee_vissrc,
2501 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2502 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2503 GST_PAD_LINK_CHECK_NOTHING);
2504 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2505 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2506 gst_object_unref (srcpad);
2509 GST_DEBUG_OBJECT (playsink, "no vis needed");
2510 if (playsink->vischain) {
2511 if (playsink->audio_tee_vissrc) {
2512 gst_element_release_request_pad (playsink->audio_tee,
2513 playsink->audio_tee_vissrc);
2514 gst_object_unref (playsink->audio_tee_vissrc);
2515 playsink->audio_tee_vissrc = NULL;
2517 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2518 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2519 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2524 GST_DEBUG_OBJECT (playsink, "adding text");
2525 if (!playsink->textchain) {
2526 GST_DEBUG_OBJECT (playsink, "creating text chain");
2527 playsink->textchain = gen_text_chain (playsink);
2529 if (playsink->textchain) {
2532 GST_DEBUG_OBJECT (playsink, "adding text chain");
2533 if (playsink->textchain->overlay)
2534 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2536 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2538 if (!playsink->text_sinkpad_stream_synchronizer) {
2539 playsink->text_sinkpad_stream_synchronizer =
2540 gst_element_get_request_pad (GST_ELEMENT_CAST
2541 (playsink->stream_synchronizer), "sink_%d");
2542 it = gst_pad_iterate_internal_links
2543 (playsink->text_sinkpad_stream_synchronizer);
2545 gst_iterator_next (it,
2546 (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2547 g_assert (playsink->text_srcpad_stream_synchronizer);
2548 gst_iterator_free (it);
2550 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2551 playsink->text_sinkpad_stream_synchronizer);
2552 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2553 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2560 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2561 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2562 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2563 GST_PAD_LINK_CHECK_NOTHING);
2564 gst_object_unref (srcpad);
2566 if (need_deinterlace)
2567 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2568 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2570 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2571 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2573 gst_pad_link_full (playsink->textchain->srcpad,
2574 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2576 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2579 GST_DEBUG_OBJECT (playsink, "no text needed");
2580 /* we have no subtitles/text or we are requested to not show them */
2582 if (playsink->text_sinkpad_stream_synchronizer) {
2583 gst_element_release_request_pad (GST_ELEMENT_CAST
2584 (playsink->stream_synchronizer),
2585 playsink->text_sinkpad_stream_synchronizer);
2586 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2587 playsink->text_sinkpad_stream_synchronizer = NULL;
2588 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2589 playsink->text_srcpad_stream_synchronizer = NULL;
2592 if (playsink->textchain) {
2593 if (playsink->text_pad == NULL) {
2594 /* no text pad, remove the chain entirely */
2595 GST_DEBUG_OBJECT (playsink, "removing text chain");
2596 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2597 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2599 /* we have a chain and a textpad, turn the subtitles off */
2600 GST_DEBUG_OBJECT (playsink, "turning off the text");
2601 if (playsink->textchain->overlay)
2602 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2606 if (!need_video && playsink->video_pad) {
2607 if (playsink->video_sinkpad_stream_synchronizer) {
2608 gst_element_release_request_pad (GST_ELEMENT_CAST
2609 (playsink->stream_synchronizer),
2610 playsink->video_sinkpad_stream_synchronizer);
2611 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2612 playsink->video_sinkpad_stream_synchronizer = NULL;
2613 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2614 playsink->video_srcpad_stream_synchronizer = NULL;
2617 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2620 if (playsink->text_pad && !playsink->textchain)
2621 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2623 update_av_offset (playsink);
2624 do_async_done (playsink);
2625 GST_PLAY_SINK_UNLOCK (playsink);
2632 /* gen_ chain already posted error */
2633 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2634 GST_PLAY_SINK_UNLOCK (playsink);
2640 * gst_play_sink_set_flags:
2641 * @playsink: a #GstPlaySink
2642 * @flags: #GstPlayFlags
2644 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2645 * when constructing the sink pipelins.
2647 * Returns: TRUE if the flags could be configured.
2650 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2652 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2654 GST_OBJECT_LOCK (playsink);
2655 playsink->flags = flags;
2656 GST_OBJECT_UNLOCK (playsink);
2662 * gst_play_sink_get_flags:
2663 * @playsink: a #GstPlaySink
2665 * Get the flags of @playsink. That flags control the behaviour of the sink when
2666 * it constructs the sink pipelines.
2668 * Returns: the currently configured #GstPlayFlags.
2671 gst_play_sink_get_flags (GstPlaySink * playsink)
2675 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2677 GST_OBJECT_LOCK (playsink);
2678 res = playsink->flags;
2679 GST_OBJECT_UNLOCK (playsink);
2685 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2687 GstPlayTextChain *chain;
2689 GST_PLAY_SINK_LOCK (playsink);
2690 chain = (GstPlayTextChain *) playsink->textchain;
2691 g_free (playsink->font_desc);
2692 playsink->font_desc = g_strdup (desc);
2693 if (chain && chain->overlay) {
2694 g_object_set (chain->overlay, "font-desc", desc, NULL);
2696 GST_PLAY_SINK_UNLOCK (playsink);
2700 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2702 gchar *result = NULL;
2703 GstPlayTextChain *chain;
2705 GST_PLAY_SINK_LOCK (playsink);
2706 chain = (GstPlayTextChain *) playsink->textchain;
2707 if (chain && chain->overlay) {
2708 g_object_get (chain->overlay, "font-desc", &result, NULL);
2709 playsink->font_desc = g_strdup (result);
2711 result = g_strdup (playsink->font_desc);
2713 GST_PLAY_SINK_UNLOCK (playsink);
2719 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2720 const gchar * encoding)
2722 GstPlayTextChain *chain;
2724 GST_PLAY_SINK_LOCK (playsink);
2725 chain = (GstPlayTextChain *) playsink->textchain;
2726 g_free (playsink->subtitle_encoding);
2727 playsink->subtitle_encoding = g_strdup (encoding);
2728 if (chain && chain->overlay) {
2729 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2731 GST_PLAY_SINK_UNLOCK (playsink);
2735 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2737 gchar *result = NULL;
2738 GstPlayTextChain *chain;
2740 GST_PLAY_SINK_LOCK (playsink);
2741 chain = (GstPlayTextChain *) playsink->textchain;
2742 if (chain && chain->overlay) {
2743 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2744 playsink->subtitle_encoding = g_strdup (result);
2746 result = g_strdup (playsink->subtitle_encoding);
2748 GST_PLAY_SINK_UNLOCK (playsink);
2754 update_av_offset (GstPlaySink * playsink)
2757 GstPlayAudioChain *achain;
2758 GstPlayVideoChain *vchain;
2760 av_offset = playsink->av_offset;
2761 achain = (GstPlayAudioChain *) playsink->audiochain;
2762 vchain = (GstPlayVideoChain *) playsink->videochain;
2764 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2765 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2766 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2768 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2773 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2775 GST_PLAY_SINK_LOCK (playsink);
2776 playsink->av_offset = av_offset;
2777 update_av_offset (playsink);
2778 GST_PLAY_SINK_UNLOCK (playsink);
2782 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2786 GST_PLAY_SINK_LOCK (playsink);
2787 result = playsink->av_offset;
2788 GST_PLAY_SINK_UNLOCK (playsink);
2794 * gst_play_sink_get_last_frame:
2795 * @playsink: a #GstPlaySink
2797 * Get the last displayed frame from @playsink. This frame is in the native
2798 * format of the sink element, the caps on the result buffer contain the format
2799 * of the frame data.
2801 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2805 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2807 GstBuffer *result = NULL;
2808 GstPlayVideoChain *chain;
2810 GST_PLAY_SINK_LOCK (playsink);
2811 GST_DEBUG_OBJECT (playsink, "taking last frame");
2812 /* get the video chain if we can */
2813 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2814 GST_DEBUG_OBJECT (playsink, "found video chain");
2815 /* see if the chain is active */
2816 if (chain->chain.activated && chain->sink) {
2819 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2821 /* find and get the last-buffer property now */
2823 gst_play_sink_find_property (playsink, chain->sink,
2824 "last-buffer", GST_TYPE_BUFFER))) {
2825 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2826 g_object_get (elem, "last-buffer", &result, NULL);
2827 gst_object_unref (elem);
2831 GST_PLAY_SINK_UNLOCK (playsink);
2837 * gst_play_sink_convert_frame:
2838 * @playsink: a #GstPlaySink
2841 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2842 * be in the native format of the sink element and the caps on the buffer
2843 * describe the format of the frame. If @caps is not %NULL, the video
2844 * frame will be converted to the format of the caps.
2846 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2847 * available or when the conversion failed.
2850 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2854 result = gst_play_sink_get_last_frame (playsink);
2855 if (result != NULL && caps != NULL) {
2859 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2860 gst_buffer_unref (result);
2861 if (temp == NULL && err) {
2862 /* I'm really uncertain whether we should make playsink post an error
2863 * on the bus or not. It's not like it's a critical issue regarding
2864 * playsink behaviour. */
2865 GST_ERROR ("Error converting frame: %s", err->message);
2873 is_raw_structure (GstStructure * s)
2877 name = gst_structure_get_name (s);
2879 if (g_str_has_prefix (name, "video/x-raw-") ||
2880 g_str_has_prefix (name, "audio/x-raw-"))
2886 is_raw_pad (GstPad * pad)
2888 GstPad *peer = gst_pad_get_peer (pad);
2890 gboolean raw = TRUE;
2895 caps = gst_pad_get_negotiated_caps (peer);
2899 caps = gst_pad_get_caps_reffed (peer);
2901 n = gst_caps_get_size (caps);
2902 for (i = 0; i < n; i++) {
2903 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2907 } else if (raw != r) {
2908 GST_ERROR_OBJECT (pad,
2909 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2915 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2917 gst_caps_unref (caps);
2918 gst_object_unref (peer);
2924 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
2926 GstPlaySink *playsink = (GstPlaySink *) user_data;
2929 GST_PLAY_SINK_LOCK (playsink);
2931 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2932 if (pad == playsink->video_pad) {
2933 playsink->video_pad_blocked = blocked;
2934 GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
2936 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2937 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2939 } else if (pad == playsink->audio_pad) {
2940 playsink->audio_pad_blocked = blocked;
2941 GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
2943 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2944 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2946 } else if (pad == playsink->text_pad) {
2947 playsink->text_pad_blocked = blocked;
2948 GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
2950 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
2954 gst_object_unref (pad);
2955 GST_PLAY_SINK_UNLOCK (playsink);
2959 /* We reconfigure when for ALL streams:
2960 * * there isn't a pad
2961 * * OR the pad is blocked
2962 * * OR there are no pending blocks on that pad
2965 if ((!playsink->video_pad || playsink->video_pad_blocked
2966 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
2967 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
2968 && (!playsink->text_pad || playsink->text_pad_blocked
2969 || !PENDING_TEXT_BLOCK (playsink))) {
2970 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
2972 if (playsink->video_pad) {
2973 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
2974 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
2975 playsink->video_pad_raw);
2978 if (playsink->audio_pad) {
2979 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
2980 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
2981 playsink->audio_pad_raw);
2984 gst_play_sink_reconfigure (playsink);
2986 if (playsink->video_pad) {
2988 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2989 (playsink->video_pad)));
2990 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
2991 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2992 gst_object_unref (opad);
2995 if (playsink->audio_pad) {
2997 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2998 (playsink->audio_pad)));
2999 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3000 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3001 gst_object_unref (opad);
3004 if (playsink->text_pad) {
3006 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3007 (playsink->text_pad)));
3008 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3009 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3010 gst_object_unref (opad);
3014 gst_object_unref (pad);
3016 GST_PLAY_SINK_UNLOCK (playsink);
3020 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3022 gboolean reconfigure = FALSE;
3026 g_object_get (pad, "caps", &caps, NULL);
3030 if (pad == playsink->audio_pad) {
3031 raw = is_raw_pad (pad);
3032 reconfigure = (!!playsink->audio_pad_raw != !!raw)
3033 && playsink->audiochain;
3034 GST_DEBUG_OBJECT (pad,
3035 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3037 } else if (pad == playsink->video_pad) {
3038 raw = is_raw_pad (pad);
3039 reconfigure = (!!playsink->video_pad_raw != !!raw)
3040 && playsink->videochain;
3041 GST_DEBUG_OBJECT (pad,
3042 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3046 gst_caps_unref (caps);
3049 GST_PLAY_SINK_LOCK (playsink);
3050 if (playsink->video_pad) {
3052 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3053 (playsink->video_pad)));
3054 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3055 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3056 gst_object_unref (opad);
3059 if (playsink->audio_pad) {
3061 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3062 (playsink->audio_pad)));
3063 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3064 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3065 gst_object_unref (opad);
3068 if (playsink->text_pad) {
3070 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3071 (playsink->text_pad)));
3072 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3073 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3074 gst_object_unref (opad);
3076 GST_PLAY_SINK_UNLOCK (playsink);
3081 * gst_play_sink_request_pad
3082 * @playsink: a #GstPlaySink
3083 * @type: a #GstPlaySinkType
3085 * Create or return a pad of @type.
3087 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3090 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3093 gboolean created = FALSE;
3094 gboolean activate = TRUE;
3095 const gchar *pad_name = NULL;
3097 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3099 GST_PLAY_SINK_LOCK (playsink);
3101 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3102 case GST_PLAY_SINK_TYPE_AUDIO:
3103 pad_name = "audio_sink";
3104 if (!playsink->audio_tee) {
3105 GST_LOG_OBJECT (playsink, "creating tee");
3106 /* create tee when needed. This element will feed the audio sink chain
3107 * and the vis chain. */
3108 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3109 if (playsink->audio_tee == NULL) {
3110 post_missing_element_message (playsink, "tee");
3111 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3112 (_("Missing element '%s' - check your GStreamer installation."),
3117 playsink->audio_tee_sink =
3118 gst_element_get_static_pad (playsink->audio_tee, "sink");
3119 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3120 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3123 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3125 if (!playsink->audio_pad) {
3126 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3127 playsink->audio_pad =
3128 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3129 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3130 G_CALLBACK (caps_notify_cb), playsink);
3133 playsink->audio_pad_raw = FALSE;
3134 res = playsink->audio_pad;
3136 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3137 case GST_PLAY_SINK_TYPE_VIDEO:
3138 pad_name = "video_sink";
3139 if (!playsink->video_pad) {
3140 GST_LOG_OBJECT (playsink, "ghosting videosink");
3141 playsink->video_pad =
3142 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3143 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3144 G_CALLBACK (caps_notify_cb), playsink);
3147 playsink->video_pad_raw = FALSE;
3148 res = playsink->video_pad;
3150 case GST_PLAY_SINK_TYPE_TEXT:
3151 GST_LOG_OBJECT (playsink, "ghosting text");
3152 if (!playsink->text_pad) {
3153 playsink->text_pad =
3154 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3157 res = playsink->text_pad;
3159 case GST_PLAY_SINK_TYPE_FLUSHING:
3163 /* we need a unique padname for the flushing pad. */
3164 padname = g_strdup_printf ("flushing_%d", playsink->count);
3165 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3176 GST_PLAY_SINK_UNLOCK (playsink);
3178 if (created && res) {
3179 /* we have to add the pad when it's active or we get an error when the
3180 * element is 'running' */
3181 gst_pad_set_active (res, TRUE);
3182 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3183 if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3185 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3187 gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3188 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3189 PENDING_FLAG_SET (playsink, type);
3190 gst_object_unref (blockpad);
3193 gst_pad_set_active (res, activate);
3200 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3205 GstPlaySinkType type;
3206 const gchar *tplname;
3208 g_return_val_if_fail (templ != NULL, NULL);
3210 GST_DEBUG_OBJECT (element, "name:%s", name);
3212 psink = GST_PLAY_SINK (element);
3213 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3215 /* Figure out the GstPlaySinkType based on the template */
3216 if (!strcmp (tplname, "audio_sink"))
3217 type = GST_PLAY_SINK_TYPE_AUDIO;
3218 else if (!strcmp (tplname, "audio_raw_sink"))
3219 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3220 else if (!strcmp (tplname, "video_sink"))
3221 type = GST_PLAY_SINK_TYPE_VIDEO;
3222 else if (!strcmp (tplname, "video_raw_sink"))
3223 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3224 else if (!strcmp (tplname, "text_sink"))
3225 type = GST_PLAY_SINK_TYPE_TEXT;
3227 goto unknown_template;
3229 pad = gst_play_sink_request_pad (psink, type);
3233 GST_WARNING_OBJECT (element, "Unknown pad template");
3238 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3240 GstPad **res = NULL;
3241 gboolean untarget = TRUE;
3243 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3245 GST_PLAY_SINK_LOCK (playsink);
3246 if (pad == playsink->video_pad) {
3247 res = &playsink->video_pad;
3248 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3250 } else if (pad == playsink->audio_pad) {
3251 res = &playsink->audio_pad;
3252 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3254 } else if (pad == playsink->text_pad) {
3255 res = &playsink->text_pad;
3257 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3261 GST_PLAY_SINK_UNLOCK (playsink);
3264 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3265 gst_pad_set_active (*res, FALSE);
3267 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3268 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3270 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3271 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3277 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3279 GstPlaySink *psink = GST_PLAY_SINK (element);
3281 gst_play_sink_release_pad (psink, pad);
3285 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3287 GstPlaySink *playsink;
3289 playsink = GST_PLAY_SINK_CAST (bin);
3291 switch (GST_MESSAGE_TYPE (message)) {
3292 case GST_MESSAGE_STEP_DONE:
3297 gboolean flush, intermediate, eos;
3300 GST_INFO_OBJECT (playsink, "Handling step-done message");
3301 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3302 &intermediate, &duration, &eos);
3304 if (format == GST_FORMAT_BUFFERS) {
3305 /* for the buffer format, we align the other streams */
3306 if (playsink->audiochain) {
3310 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3313 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3314 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3318 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3322 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3327 /* Send an event to our sinks until one of them works; don't then send to the
3328 * remaining sinks (unlike GstBin)
3329 * Special case: If a text sink is set we need to send the event
3330 * to them in case it's source is different from the a/v stream's source.
3333 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3335 gboolean res = TRUE;
3337 if (playsink->textchain && playsink->textchain->sink) {
3338 gst_event_ref (event);
3339 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3340 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3342 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3346 if (playsink->videochain) {
3347 gst_event_ref (event);
3348 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3349 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3352 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3354 if (playsink->audiochain) {
3355 gst_event_ref (event);
3356 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3357 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3360 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3364 gst_event_unref (event);
3368 /* We only want to send the event to a single sink (overriding GstBin's
3369 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3370 * events appropriately. So, this is a messy duplication of code. */
3372 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3374 gboolean res = FALSE;
3375 GstEventType event_type = GST_EVENT_TYPE (event);
3376 GstPlaySink *playsink;
3378 playsink = GST_PLAY_SINK_CAST (element);
3380 switch (event_type) {
3381 case GST_EVENT_SEEK:
3382 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3383 res = gst_play_sink_send_event_to_sink (playsink, event);
3385 case GST_EVENT_STEP:
3390 gboolean flush, intermediate;
3392 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3395 if (format == GST_FORMAT_BUFFERS) {
3396 /* for buffers, we will try to step video frames, for other formats we
3397 * send the step to all sinks */
3398 res = gst_play_sink_send_event_to_sink (playsink, event);
3401 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3408 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3415 static GstStateChangeReturn
3416 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3418 GstStateChangeReturn ret;
3419 GstStateChangeReturn bret;
3421 GstPlaySink *playsink;
3423 playsink = GST_PLAY_SINK (element);
3425 switch (transition) {
3426 case GST_STATE_CHANGE_READY_TO_PAUSED:
3427 playsink->need_async_start = TRUE;
3428 /* we want to go async to PAUSED until we managed to configure and add the
3430 do_async_start (playsink);
3431 ret = GST_STATE_CHANGE_ASYNC;
3433 case GST_STATE_CHANGE_PAUSED_TO_READY:
3434 /* unblock all pads here */
3435 GST_PLAY_SINK_LOCK (playsink);
3436 if (playsink->video_pad) {
3438 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3439 (playsink->video_pad)));
3440 if (gst_pad_is_blocked (opad)) {
3441 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3442 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3444 gst_object_unref (opad);
3445 playsink->video_pad_blocked = FALSE;
3448 if (playsink->audio_pad) {
3450 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3451 (playsink->audio_pad)));
3453 if (gst_pad_is_blocked (opad)) {
3454 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3455 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3457 gst_object_unref (opad);
3458 playsink->audio_pad_blocked = FALSE;
3461 if (playsink->text_pad) {
3463 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3464 (playsink->text_pad)));
3465 if (gst_pad_is_blocked (opad)) {
3466 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3467 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3469 gst_object_unref (opad);
3470 playsink->text_pad_blocked = FALSE;
3472 GST_PLAY_SINK_UNLOCK (playsink);
3474 case GST_STATE_CHANGE_READY_TO_NULL:
3475 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3476 /* remove our links to the mute and volume elements when they were
3477 * provided by a sink */
3478 disconnect_chain (playsink->audiochain, playsink);
3479 playsink->audiochain->volume = NULL;
3480 playsink->audiochain->mute = NULL;
3483 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3484 gst_object_unref (playsink->audiochain->ts_offset);
3485 playsink->audiochain->ts_offset = NULL;
3488 if (playsink->videochain && playsink->videochain->ts_offset) {
3489 gst_object_unref (playsink->videochain->ts_offset);
3490 playsink->videochain->ts_offset = NULL;
3492 ret = GST_STATE_CHANGE_SUCCESS;
3495 /* all other state changes return SUCCESS by default, this value can be
3496 * overridden by the result of the children */
3497 ret = GST_STATE_CHANGE_SUCCESS;
3501 /* do the state change of the children */
3503 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3505 /* now look at the result of our children and adjust the return value */
3507 case GST_STATE_CHANGE_FAILURE:
3508 /* failure, we stop */
3509 goto activate_failed;
3510 case GST_STATE_CHANGE_NO_PREROLL:
3511 /* some child returned NO_PREROLL. This is strange but we never know. We
3512 * commit our async state change (if any) and return the NO_PREROLL */
3513 do_async_done (playsink);
3516 case GST_STATE_CHANGE_ASYNC:
3517 /* some child was async, return this */
3521 /* return our previously configured return value */
3525 switch (transition) {
3526 case GST_STATE_CHANGE_READY_TO_PAUSED:
3528 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3529 /* FIXME Release audio device when we implement that */
3530 playsink->need_async_start = TRUE;
3532 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3533 if (playsink->video_sinkpad_stream_synchronizer) {
3534 gst_element_release_request_pad (GST_ELEMENT_CAST
3535 (playsink->stream_synchronizer),
3536 playsink->video_sinkpad_stream_synchronizer);
3537 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3538 playsink->video_sinkpad_stream_synchronizer = NULL;
3539 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3540 playsink->video_srcpad_stream_synchronizer = NULL;
3542 if (playsink->audio_sinkpad_stream_synchronizer) {
3543 gst_element_release_request_pad (GST_ELEMENT_CAST
3544 (playsink->stream_synchronizer),
3545 playsink->audio_sinkpad_stream_synchronizer);
3546 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3547 playsink->audio_sinkpad_stream_synchronizer = NULL;
3548 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3549 playsink->audio_srcpad_stream_synchronizer = NULL;
3551 if (playsink->text_sinkpad_stream_synchronizer) {
3552 gst_element_release_request_pad (GST_ELEMENT_CAST
3553 (playsink->stream_synchronizer),
3554 playsink->text_sinkpad_stream_synchronizer);
3555 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3556 playsink->text_sinkpad_stream_synchronizer = NULL;
3557 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3558 playsink->text_srcpad_stream_synchronizer = NULL;
3562 case GST_STATE_CHANGE_READY_TO_NULL:
3563 /* remove sinks we added */
3564 if (playsink->videodeinterlacechain) {
3565 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3567 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3569 if (playsink->videochain) {
3570 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3571 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3573 if (playsink->audiochain) {
3574 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3575 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3577 if (playsink->vischain) {
3578 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3579 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3581 if (playsink->textchain) {
3582 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3583 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3585 do_async_done (playsink);
3586 /* when going to READY, keep elements around as long as possible,
3587 * so they may be re-used faster next time/url around.
3588 * when really going to NULL, clean up everything completely. */
3589 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3591 /* Unparent the sinks to allow reuse */
3592 if (playsink->videochain && playsink->videochain->sink)
3593 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3594 playsink->videochain->sink);
3595 if (playsink->audiochain && playsink->audiochain->sink)
3596 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3597 playsink->audiochain->sink);
3598 if (playsink->textchain && playsink->textchain->sink)
3599 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3600 playsink->textchain->sink);
3602 if (playsink->audio_sink != NULL)
3603 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3604 if (playsink->video_sink != NULL)
3605 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3606 if (playsink->visualisation != NULL)
3607 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3608 if (playsink->text_sink != NULL)
3609 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3611 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3612 playsink->videodeinterlacechain = NULL;
3613 free_chain ((GstPlayChain *) playsink->videochain);
3614 playsink->videochain = NULL;
3615 free_chain ((GstPlayChain *) playsink->audiochain);
3616 playsink->audiochain = NULL;
3617 free_chain ((GstPlayChain *) playsink->vischain);
3618 playsink->vischain = NULL;
3619 free_chain ((GstPlayChain *) playsink->textchain);
3620 playsink->textchain = NULL;
3631 GST_DEBUG_OBJECT (element,
3632 "element failed to change states -- activation problem?");
3633 return GST_STATE_CHANGE_FAILURE;
3638 gst_play_sink_set_property (GObject * object, guint prop_id,
3639 const GValue * value, GParamSpec * spec)
3641 GstPlaySink *playsink = GST_PLAY_SINK (object);
3645 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3648 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3651 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3653 case PROP_FONT_DESC:
3654 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3656 case PROP_SUBTITLE_ENCODING:
3657 gst_play_sink_set_subtitle_encoding (playsink,
3658 g_value_get_string (value));
3660 case PROP_VIS_PLUGIN:
3661 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3663 case PROP_AV_OFFSET:
3664 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3666 case PROP_VIDEO_SINK:
3667 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3668 g_value_get_object (value));
3670 case PROP_AUDIO_SINK:
3671 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3672 g_value_get_object (value));
3674 case PROP_TEXT_SINK:
3675 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3676 g_value_get_object (value));
3679 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3685 gst_play_sink_get_property (GObject * object, guint prop_id,
3686 GValue * value, GParamSpec * spec)
3688 GstPlaySink *playsink = GST_PLAY_SINK (object);
3692 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3695 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3698 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3700 case PROP_FONT_DESC:
3701 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3703 case PROP_SUBTITLE_ENCODING:
3704 g_value_take_string (value,
3705 gst_play_sink_get_subtitle_encoding (playsink));
3707 case PROP_VIS_PLUGIN:
3708 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3711 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3713 case PROP_AV_OFFSET:
3714 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3716 case PROP_VIDEO_SINK:
3717 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3718 GST_PLAY_SINK_TYPE_VIDEO));
3720 case PROP_AUDIO_SINK:
3721 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3722 GST_PLAY_SINK_TYPE_AUDIO));
3724 case PROP_TEXT_SINK:
3725 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3726 GST_PLAY_SINK_TYPE_TEXT));
3729 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3736 gst_play_sink_plugin_init (GstPlugin * plugin)
3738 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3740 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3741 GST_TYPE_PLAY_SINK);