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,
263 static void gst_play_sink_dispose (GObject * object);
264 static void gst_play_sink_finalize (GObject * object);
265 static void gst_play_sink_set_property (GObject * object, guint prop_id,
266 const GValue * value, GParamSpec * spec);
267 static void gst_play_sink_get_property (GObject * object, guint prop_id,
268 GValue * value, GParamSpec * spec);
270 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
271 GstPadTemplate * templ, const gchar * name);
272 static void gst_play_sink_release_request_pad (GstElement * element,
274 static gboolean gst_play_sink_send_event (GstElement * element,
276 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
277 GstStateChange transition);
279 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
281 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
282 GstPlaySink * playsink);
283 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
284 GstPlaySink * playsink);
286 static void update_av_offset (GstPlaySink * playsink);
289 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
290 GValue * return_value G_GNUC_UNUSED,
291 guint n_param_values,
292 const GValue * param_values,
293 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
295 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
296 gpointer arg_1, gpointer data2);
297 register GMarshalFunc_OBJECT__BOXED callback;
298 register GCClosure *cc = (GCClosure *) closure;
299 register gpointer data1, data2;
301 g_return_if_fail (return_value != NULL);
302 g_return_if_fail (n_param_values == 2);
304 if (G_CCLOSURE_SWAP_DATA (closure)) {
305 data1 = closure->data;
306 data2 = g_value_peek_pointer (param_values + 0);
308 data1 = g_value_peek_pointer (param_values + 0);
309 data2 = closure->data;
312 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
314 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
316 gst_value_take_buffer (return_value, v_return);
319 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
321 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
324 gst_play_sink_class_init (GstPlaySinkClass * klass)
326 GObjectClass *gobject_klass;
327 GstElementClass *gstelement_klass;
328 GstBinClass *gstbin_klass;
330 gobject_klass = (GObjectClass *) klass;
331 gstelement_klass = (GstElementClass *) klass;
332 gstbin_klass = (GstBinClass *) klass;
334 gobject_klass->dispose = gst_play_sink_dispose;
335 gobject_klass->finalize = gst_play_sink_finalize;
336 gobject_klass->set_property = gst_play_sink_set_property;
337 gobject_klass->get_property = gst_play_sink_get_property;
343 * Control the behaviour of playsink.
345 g_object_class_install_property (gobject_klass, PROP_FLAGS,
346 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
347 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
348 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
351 * GstPlaySink:volume:
353 * Get or set the current audio stream volume. 1.0 means 100%,
354 * 0.0 means mute. This uses a linear volume scale.
357 g_object_class_install_property (gobject_klass, PROP_VOLUME,
358 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
359 0.0, VOLUME_MAX_DOUBLE, 1.0,
360 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
361 g_object_class_install_property (gobject_klass, PROP_MUTE,
362 g_param_spec_boolean ("mute", "Mute",
363 "Mute the audio channel without changing the volume", FALSE,
364 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
365 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
366 g_param_spec_string ("subtitle-font-desc",
367 "Subtitle font description",
368 "Pango font description of font "
369 "to be used for subtitle rendering", NULL,
370 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
371 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
372 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
373 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
374 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
375 "be checked for an encoding to use. If that is not set either, "
376 "ISO-8859-15 will be assumed.", NULL,
377 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
378 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
379 g_param_spec_object ("vis-plugin", "Vis plugin",
380 "the visualization element to use (NULL = default)",
381 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
385 * Get the currently rendered or prerolled frame in the video sink.
386 * The #GstCaps on the buffer will describe the format of the buffer.
390 g_object_class_install_property (gobject_klass, PROP_FRAME,
391 gst_param_spec_mini_object ("frame", "Frame",
392 "The last frame (NULL = no video available)",
393 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
395 * GstPlaySink:av-offset:
397 * Control the synchronisation offset between the audio and video streams.
398 * Positive values make the audio ahead of the video and negative values make
399 * the audio go behind the video.
403 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
404 g_param_spec_int64 ("av-offset", "AV Offset",
405 "The synchronisation offset between audio and video in nanoseconds",
406 G_MININT64, G_MAXINT64, 0,
407 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
409 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
410 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
411 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
414 * GstPlaySink::convert-frame
415 * @playsink: a #GstPlaySink
416 * @caps: the target format of the frame
418 * Action signal to retrieve the currently playing video frame in the format
419 * specified by @caps.
420 * If @caps is %NULL, no conversion will be performed and this function is
421 * equivalent to the #GstPlaySink::frame property.
423 * Returns: a #GstBuffer of the current video frame converted to #caps.
424 * The caps on the buffer will describe the final layout of the buffer data.
425 * %NULL is returned when no current buffer can be retrieved or when the
430 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
431 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
432 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
433 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
435 gst_element_class_add_pad_template (gstelement_klass,
436 gst_static_pad_template_get (&audiorawtemplate));
437 gst_element_class_add_pad_template (gstelement_klass,
438 gst_static_pad_template_get (&audiotemplate));
439 gst_element_class_add_pad_template (gstelement_klass,
440 gst_static_pad_template_get (&videorawtemplate));
441 gst_element_class_add_pad_template (gstelement_klass,
442 gst_static_pad_template_get (&videotemplate));
443 gst_element_class_add_pad_template (gstelement_klass,
444 gst_static_pad_template_get (&texttemplate));
445 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
447 "Convenience sink for multiple streams",
448 "Wim Taymans <wim.taymans@gmail.com>");
450 gstelement_klass->change_state =
451 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
452 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
453 gstelement_klass->request_new_pad =
454 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
455 gstelement_klass->release_pad =
456 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
458 gstbin_klass->handle_message =
459 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
461 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
462 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
466 gst_play_sink_init (GstPlaySink * playsink)
469 playsink->video_sink = NULL;
470 playsink->audio_sink = NULL;
471 playsink->visualisation = NULL;
472 playsink->text_sink = NULL;
473 playsink->volume = 1.0;
474 playsink->font_desc = NULL;
475 playsink->subtitle_encoding = NULL;
476 playsink->flags = DEFAULT_FLAGS;
478 playsink->stream_synchronizer =
479 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
480 gst_bin_add (GST_BIN_CAST (playsink),
481 GST_ELEMENT_CAST (playsink->stream_synchronizer));
483 g_static_rec_mutex_init (&playsink->lock);
484 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
488 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
492 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
495 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
501 free_chain (GstPlayChain * chain)
505 gst_object_unref (chain->bin);
511 gst_play_sink_dispose (GObject * object)
513 GstPlaySink *playsink;
515 playsink = GST_PLAY_SINK (object);
517 if (playsink->audio_sink != NULL) {
518 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
519 gst_object_unref (playsink->audio_sink);
520 playsink->audio_sink = NULL;
522 if (playsink->video_sink != NULL) {
523 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
524 gst_object_unref (playsink->video_sink);
525 playsink->video_sink = NULL;
527 if (playsink->visualisation != NULL) {
528 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
529 gst_object_unref (playsink->visualisation);
530 playsink->visualisation = NULL;
532 if (playsink->text_sink != NULL) {
533 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
534 gst_object_unref (playsink->text_sink);
535 playsink->text_sink = NULL;
538 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
539 playsink->videodeinterlacechain = NULL;
540 free_chain ((GstPlayChain *) playsink->videochain);
541 playsink->videochain = NULL;
542 free_chain ((GstPlayChain *) playsink->audiochain);
543 playsink->audiochain = NULL;
544 free_chain ((GstPlayChain *) playsink->vischain);
545 playsink->vischain = NULL;
546 free_chain ((GstPlayChain *) playsink->textchain);
547 playsink->textchain = NULL;
549 if (playsink->audio_tee_sink) {
550 gst_object_unref (playsink->audio_tee_sink);
551 playsink->audio_tee_sink = NULL;
554 if (playsink->audio_tee_vissrc) {
555 gst_element_release_request_pad (playsink->audio_tee,
556 playsink->audio_tee_vissrc);
557 gst_object_unref (playsink->audio_tee_vissrc);
558 playsink->audio_tee_vissrc = NULL;
561 if (playsink->audio_tee_asrc) {
562 gst_element_release_request_pad (playsink->audio_tee,
563 playsink->audio_tee_asrc);
564 gst_object_unref (playsink->audio_tee_asrc);
565 playsink->audio_tee_asrc = NULL;
568 g_free (playsink->font_desc);
569 playsink->font_desc = NULL;
571 g_free (playsink->subtitle_encoding);
572 playsink->subtitle_encoding = NULL;
574 playsink->stream_synchronizer = NULL;
576 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
580 gst_play_sink_finalize (GObject * object)
582 GstPlaySink *playsink;
584 playsink = GST_PLAY_SINK (object);
586 g_static_rec_mutex_free (&playsink->lock);
588 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
592 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
595 GstElement **elem = NULL, *old = NULL;
597 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
599 GST_PLAY_SINK_LOCK (playsink);
601 case GST_PLAY_SINK_TYPE_AUDIO:
602 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
603 elem = &playsink->audio_sink;
605 case GST_PLAY_SINK_TYPE_VIDEO:
606 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
607 elem = &playsink->video_sink;
609 case GST_PLAY_SINK_TYPE_TEXT:
610 elem = &playsink->text_sink;
618 gst_object_ref (sink);
621 GST_PLAY_SINK_UNLOCK (playsink);
625 gst_element_set_state (old, GST_STATE_NULL);
626 gst_object_unref (old);
631 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
633 GstElement *result = NULL;
634 GstElement *elem = NULL, *chainp = NULL;
636 GST_PLAY_SINK_LOCK (playsink);
638 case GST_PLAY_SINK_TYPE_AUDIO:
639 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
641 GstPlayAudioChain *chain;
642 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
643 chainp = chain->sink;
644 elem = playsink->audio_sink;
647 case GST_PLAY_SINK_TYPE_VIDEO:
648 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
650 GstPlayVideoChain *chain;
651 if ((chain = (GstPlayVideoChain *) playsink->videochain))
652 chainp = chain->sink;
653 elem = playsink->video_sink;
656 case GST_PLAY_SINK_TYPE_TEXT:
658 GstPlayTextChain *chain;
659 if ((chain = (GstPlayTextChain *) playsink->textchain))
660 chainp = chain->sink;
661 elem = playsink->text_sink;
668 /* we have an active chain with a sink, get the sink */
669 result = gst_object_ref (chainp);
671 /* nothing found, return last configured sink */
672 if (result == NULL && elem)
673 result = gst_object_ref (elem);
674 GST_PLAY_SINK_UNLOCK (playsink);
680 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
683 GstPlaySink *playsink;
685 playsink = GST_PLAY_SINK (user_data);
686 /* nothing to do here, we need a dummy callback here to make the async call
688 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
692 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
695 GstPlaySink *playsink;
696 GstPlayVisChain *chain;
698 playsink = GST_PLAY_SINK (user_data);
700 GST_PLAY_SINK_LOCK (playsink);
701 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
702 /* now try to change the plugin in the running vis chain */
703 if (!(chain = (GstPlayVisChain *) playsink->vischain))
706 /* unlink the old plugin and unghost the pad */
707 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
708 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
710 /* set the old plugin to NULL and remove */
711 gst_element_set_state (chain->vis, GST_STATE_NULL);
712 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
714 /* add new plugin and set state to playing */
715 chain->vis = playsink->visualisation;
716 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
717 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
720 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
721 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
724 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
725 GST_PAD_LINK_CHECK_NOTHING);
726 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
730 /* Unblock the pad */
731 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
733 GST_PLAY_SINK_UNLOCK (playsink);
737 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
739 GstPlayVisChain *chain;
741 /* setting NULL means creating the default vis plugin */
743 vis = gst_element_factory_make ("goom", "vis");
745 /* simply return if we don't have a vis plugin here */
749 GST_PLAY_SINK_LOCK (playsink);
750 /* first store the new vis */
751 if (playsink->visualisation)
752 gst_object_unref (playsink->visualisation);
754 gst_object_ref_sink (vis);
755 playsink->visualisation = vis;
757 /* now try to change the plugin in the running vis chain, if we have no chain,
758 * we don't bother, any future vis chain will be created with the new vis
760 if (!(chain = (GstPlayVisChain *) playsink->vischain))
763 /* block the pad, the next time the callback is called we can change the
764 * visualisation. It's possible that this never happens or that the pad was
765 * already blocked. If the callback never happens, we don't have new data so
766 * we don't need the new vis plugin. If the pad was already blocked, the
767 * function returns FALSE but the previous pad block will do the right thing
769 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
770 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
773 GST_PLAY_SINK_UNLOCK (playsink);
779 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
781 GstElement *result = NULL;
782 GstPlayVisChain *chain;
784 GST_PLAY_SINK_LOCK (playsink);
785 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
786 /* we have an active chain, get the sink */
788 result = gst_object_ref (chain->vis);
790 /* nothing found, return last configured sink */
791 if (result == NULL && playsink->visualisation)
792 result = gst_object_ref (playsink->visualisation);
793 GST_PLAY_SINK_UNLOCK (playsink);
799 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
801 GstPlayAudioChain *chain;
803 GST_PLAY_SINK_LOCK (playsink);
804 playsink->volume = volume;
805 chain = (GstPlayAudioChain *) playsink->audiochain;
806 if (chain && chain->volume) {
807 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
808 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
809 chain->mute, volume, playsink->mute);
810 /* if there is a mute element or we are not muted, set the volume */
811 if (chain->mute || !playsink->mute)
812 g_object_set (chain->volume, "volume", volume, NULL);
814 GST_LOG_OBJECT (playsink, "no volume element");
815 playsink->volume_changed = TRUE;
817 GST_PLAY_SINK_UNLOCK (playsink);
821 gst_play_sink_get_volume (GstPlaySink * playsink)
824 GstPlayAudioChain *chain;
826 GST_PLAY_SINK_LOCK (playsink);
827 chain = (GstPlayAudioChain *) playsink->audiochain;
828 result = playsink->volume;
829 if (chain && chain->volume) {
830 if (chain->mute || !playsink->mute) {
831 g_object_get (chain->volume, "volume", &result, NULL);
832 playsink->volume = result;
835 GST_PLAY_SINK_UNLOCK (playsink);
841 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
843 GstPlayAudioChain *chain;
845 GST_PLAY_SINK_LOCK (playsink);
846 playsink->mute = mute;
847 chain = (GstPlayAudioChain *) playsink->audiochain;
850 g_object_set (chain->mute, "mute", mute, NULL);
851 } else if (chain->volume) {
853 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
855 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
859 playsink->mute_changed = TRUE;
861 GST_PLAY_SINK_UNLOCK (playsink);
865 gst_play_sink_get_mute (GstPlaySink * playsink)
868 GstPlayAudioChain *chain;
870 GST_PLAY_SINK_LOCK (playsink);
871 chain = (GstPlayAudioChain *) playsink->audiochain;
872 if (chain && chain->mute) {
873 g_object_get (chain->mute, "mute", &result, NULL);
874 playsink->mute = result;
876 result = playsink->mute;
878 GST_PLAY_SINK_UNLOCK (playsink);
884 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
888 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
889 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
893 add_chain (GstPlayChain * chain, gboolean add)
895 if (chain->added == add)
899 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
901 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
902 /* we don't want to lose our sink status */
903 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
912 activate_chain (GstPlayChain * chain, gboolean activate)
916 if (chain->activated == activate)
919 GST_OBJECT_LOCK (chain->playsink);
920 state = GST_STATE_TARGET (chain->playsink);
921 GST_OBJECT_UNLOCK (chain->playsink);
924 gst_element_set_state (chain->bin, state);
926 gst_element_set_state (chain->bin, GST_STATE_NULL);
928 chain->activated = activate;
934 element_is_sink (GstElement * element)
938 GST_OBJECT_LOCK (element);
939 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
940 GST_OBJECT_UNLOCK (element);
942 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
947 element_has_property (GstElement * element, const gchar * pname, GType type)
951 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
954 GST_DEBUG_OBJECT (element, "no %s property", pname);
958 if (type == G_TYPE_INVALID || type == pspec->value_type ||
959 g_type_is_a (pspec->value_type, type)) {
960 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
961 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
965 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
966 "and we expected it to be of type %s", pname,
967 g_type_name (pspec->value_type), g_type_name (type));
974 const gchar *prop_name;
977 } FindPropertyHelper;
980 find_property (GstElement * element, FindPropertyHelper * helper)
982 if (helper->need_sink && !element_is_sink (element)) {
983 gst_object_unref (element);
987 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
988 gst_object_unref (element);
992 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
993 (helper->need_sink) ? "sink" : "element");
994 return 0; /* keep it */
997 /* FIXME: why not move these functions into core? */
998 /* find a sink in the hierarchy with a property named @name. This function does
999 * not increase the refcount of the returned object and thus remains valid as
1000 * long as the bin is valid. */
1002 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1003 const gchar * name, GType expected_type)
1005 GstElement *result = NULL;
1008 if (element_has_property (obj, name, expected_type)) {
1010 } else if (GST_IS_BIN (obj)) {
1011 FindPropertyHelper helper = { name, expected_type, TRUE };
1013 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1014 result = gst_iterator_find_custom (it,
1015 (GCompareFunc) find_property, &helper);
1016 gst_iterator_free (it);
1017 /* we don't need the extra ref */
1019 gst_object_unref (result);
1024 /* find an object in the hierarchy with a property named @name */
1026 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1027 const gchar * name, GType expected_type)
1029 GstElement *result = NULL;
1032 if (GST_IS_BIN (obj)) {
1033 FindPropertyHelper helper = { name, expected_type, FALSE };
1035 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1036 result = gst_iterator_find_custom (it,
1037 (GCompareFunc) find_property, &helper);
1038 gst_iterator_free (it);
1040 if (element_has_property (obj, name, expected_type)) {
1042 gst_object_ref (obj);
1049 do_async_start (GstPlaySink * playsink)
1051 GstMessage *message;
1053 if (!playsink->need_async_start) {
1054 GST_INFO_OBJECT (playsink, "no async_start needed");
1058 playsink->async_pending = TRUE;
1060 GST_INFO_OBJECT (playsink, "Sending async_start message");
1061 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1062 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1063 (playsink), message);
1067 do_async_done (GstPlaySink * playsink)
1069 GstMessage *message;
1071 if (playsink->async_pending) {
1072 GST_INFO_OBJECT (playsink, "Sending async_done message");
1073 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1074 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1075 (playsink), message);
1077 playsink->async_pending = FALSE;
1080 playsink->need_async_start = FALSE;
1083 /* try to change the state of an element. This function returns the element when
1084 * the state change could be performed. When this function returns NULL an error
1085 * occured and the element is unreffed if @unref is TRUE. */
1087 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1089 GstStateChangeReturn ret;
1092 ret = gst_element_set_state (element, GST_STATE_READY);
1093 if (ret == GST_STATE_CHANGE_FAILURE) {
1094 GST_DEBUG_OBJECT (playsink, "failed state change..");
1095 gst_element_set_state (element, GST_STATE_NULL);
1097 gst_object_unref (element);
1104 /* make the element (bin) that contains the elements needed to perform
1105 * video display. Only used for *raw* video streams.
1107 * +------------------------------------------------------------+
1109 * | +-------+ +----------+ +----------+ +---------+ |
1110 * | | queue | |colorspace| |videoscale| |videosink| |
1111 * | +-sink src-sink src-sink src-sink | |
1112 * | | +-------+ +----------+ +----------+ +---------+ |
1114 * +------------------------------------------------------------+
1117 static GstPlayVideoDeinterlaceChain *
1118 gen_video_deinterlace_chain (GstPlaySink * playsink)
1120 GstPlayVideoDeinterlaceChain *chain;
1123 GstElement *head = NULL, *prev = NULL;
1125 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1126 chain->chain.playsink = playsink;
1128 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1130 /* create a bin to hold objects, as we create them we add them to this bin so
1131 * that when something goes wrong we only need to unref the bin */
1132 chain->chain.bin = gst_bin_new ("vdbin");
1133 bin = GST_BIN_CAST (chain->chain.bin);
1134 gst_object_ref_sink (bin);
1136 GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
1137 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vdconv");
1138 if (chain->conv == NULL) {
1139 post_missing_element_message (playsink, "ffmpegcolorspace");
1140 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1141 (_("Missing element '%s' - check your GStreamer installation."),
1142 "ffmpegcolorspace"), ("video rendering might fail"));
1144 gst_bin_add (bin, chain->conv);
1149 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1150 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1151 if (chain->deinterlace == NULL) {
1152 post_missing_element_message (playsink, "deinterlace");
1153 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1154 (_("Missing element '%s' - check your GStreamer installation."),
1155 "deinterlace"), ("deinterlacing won't work"));
1157 gst_bin_add (bin, chain->deinterlace);
1159 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1160 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1163 head = chain->deinterlace;
1165 prev = chain->deinterlace;
1169 pad = gst_element_get_static_pad (head, "sink");
1170 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1171 gst_object_unref (pad);
1173 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1177 pad = gst_element_get_static_pad (prev, "src");
1178 chain->srcpad = gst_ghost_pad_new ("src", pad);
1179 gst_object_unref (pad);
1181 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1184 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1185 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1191 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1192 (NULL), ("Failed to configure the video deinterlace chain."));
1193 free_chain ((GstPlayChain *) chain);
1198 /* make the element (bin) that contains the elements needed to perform
1201 * +------------------------------------------------------------+
1203 * | +-------+ +----------+ +----------+ +---------+ |
1204 * | | queue | |colorspace| |videoscale| |videosink| |
1205 * | +-sink src-sink src-sink src-sink | |
1206 * | | +-------+ +----------+ +----------+ +---------+ |
1208 * +------------------------------------------------------------+
1211 static GstPlayVideoChain *
1212 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1214 GstPlayVideoChain *chain;
1217 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1219 chain = g_new0 (GstPlayVideoChain, 1);
1220 chain->chain.playsink = playsink;
1221 chain->chain.raw = raw;
1223 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1225 if (playsink->video_sink) {
1226 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1227 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1229 /* only try fallback if no specific sink was chosen */
1230 if (chain->sink == NULL) {
1231 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1232 elem = gst_element_factory_make ("autovideosink", "videosink");
1233 chain->sink = try_element (playsink, elem, TRUE);
1235 if (chain->sink == NULL) {
1236 /* if default sink from config.h is different then try it too */
1237 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1238 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1239 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1240 chain->sink = try_element (playsink, elem, TRUE);
1244 playsink->video_sink = gst_object_ref (chain->sink);
1246 if (chain->sink == NULL)
1250 /* if we can disable async behaviour of the sink, we can avoid adding a
1251 * queue for the audio chain. */
1253 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1256 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1257 async, GST_ELEMENT_NAME (elem));
1258 g_object_set (elem, "async", async, NULL);
1259 chain->async = async;
1261 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1262 chain->async = TRUE;
1265 /* find ts-offset element */
1266 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1267 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1270 /* create a bin to hold objects, as we create them we add them to this bin so
1271 * that when something goes wrong we only need to unref the bin */
1272 chain->chain.bin = gst_bin_new ("vbin");
1273 bin = GST_BIN_CAST (chain->chain.bin);
1274 gst_object_ref_sink (bin);
1275 gst_bin_add (bin, chain->sink);
1277 /* decouple decoder from sink, this improves playback quite a lot since the
1278 * decoder can continue while the sink blocks for synchronisation. We don't
1279 * need a lot of buffers as this consumes a lot of memory and we don't want
1280 * too little because else we would be context switching too quickly. */
1281 chain->queue = gst_element_factory_make ("queue", "vqueue");
1282 if (chain->queue == NULL) {
1283 post_missing_element_message (playsink, "queue");
1284 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1285 (_("Missing element '%s' - check your GStreamer installation."),
1286 "queue"), ("video rendering might be suboptimal"));
1290 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1291 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1292 gst_bin_add (bin, chain->queue);
1293 head = prev = chain->queue;
1296 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1297 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1299 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1300 gst_bin_add (bin, chain->conv);
1302 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1303 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1312 GST_DEBUG_OBJECT (playsink, "linking to sink");
1313 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1314 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1318 pad = gst_element_get_static_pad (head, "sink");
1319 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1320 gst_object_unref (pad);
1322 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1329 if (!elem && !playsink->video_sink) {
1330 post_missing_element_message (playsink, "autovideosink");
1331 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1332 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1333 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1334 (_("Both autovideosink and %s elements are missing."),
1335 DEFAULT_VIDEOSINK), (NULL));
1337 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1338 (_("The autovideosink element is missing.")), (NULL));
1341 if (playsink->video_sink) {
1342 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1343 (_("Configured videosink %s is not working."),
1344 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1345 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1346 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1347 (_("Both autovideosink and %s elements are not working."),
1348 DEFAULT_VIDEOSINK), (NULL));
1350 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1351 (_("The autovideosink element is not working.")), (NULL));
1354 free_chain ((GstPlayChain *) chain);
1359 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1360 (NULL), ("Failed to configure the video sink."));
1361 /* checking sink made it READY */
1362 gst_element_set_state (chain->sink, GST_STATE_NULL);
1363 /* Remove chain from the bin to allow reuse later */
1364 gst_bin_remove (bin, chain->sink);
1365 free_chain ((GstPlayChain *) chain);
1371 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1374 GstPlayVideoChain *chain;
1375 GstStateChangeReturn ret;
1377 chain = playsink->videochain;
1379 chain->chain.raw = raw;
1381 /* if the chain was active we don't do anything */
1382 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1385 /* try to set the sink element to READY again */
1386 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1387 if (ret == GST_STATE_CHANGE_FAILURE)
1390 /* find ts-offset element */
1392 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1393 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1396 /* if we can disable async behaviour of the sink, we can avoid adding a
1397 * queue for the audio chain. */
1399 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1402 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1403 async, GST_ELEMENT_NAME (elem));
1404 g_object_set (elem, "async", async, NULL);
1405 chain->async = async;
1407 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1408 chain->async = TRUE;
1413 /* make an element for playback of video with subtitles embedded.
1414 * Only used for *raw* video streams.
1416 * +--------------------------------------------+
1418 * | +--------+ +-----------------+ |
1419 * | | queue | | subtitleoverlay | |
1420 * video--src sink---video_sink | |
1421 * | +--------+ | src--src
1422 * text------------------text_sink | |
1423 * | +-----------------+ |
1424 * +--------------------------------------------+
1427 static GstPlayTextChain *
1428 gen_text_chain (GstPlaySink * playsink)
1430 GstPlayTextChain *chain;
1433 GstPad *videosinkpad, *textsinkpad, *srcpad;
1435 chain = g_new0 (GstPlayTextChain, 1);
1436 chain->chain.playsink = playsink;
1438 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1440 chain->chain.bin = gst_bin_new ("tbin");
1441 bin = GST_BIN_CAST (chain->chain.bin);
1442 gst_object_ref_sink (bin);
1444 videosinkpad = textsinkpad = srcpad = NULL;
1446 /* first try to hook the text pad to the custom sink */
1447 if (playsink->text_sink) {
1448 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1449 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1452 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1455 /* make sure the sparse subtitles don't participate in the preroll */
1456 g_object_set (elem, "async", FALSE, NULL);
1457 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1458 gst_bin_add (bin, chain->sink);
1459 /* NOTE streamsynchronizer needs streams decoupled */
1460 /* make a little queue */
1461 chain->queue = gst_element_factory_make ("queue", "subqueue");
1462 if (chain->queue == NULL) {
1463 post_missing_element_message (playsink, "queue");
1464 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1465 (_("Missing element '%s' - check your GStreamer installation."),
1466 "queue"), ("rendering might be suboptimal"));
1468 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1469 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1470 "silent", TRUE, NULL);
1471 gst_bin_add (bin, chain->queue);
1473 /* we have a custom sink, this will be our textsinkpad */
1474 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1475 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1476 /* we're all fine now and we can add the sink to the chain */
1477 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1478 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1480 GST_WARNING_OBJECT (playsink,
1481 "can't find a sink pad on custom text sink");
1482 gst_bin_remove (bin, chain->sink);
1483 gst_bin_remove (bin, chain->queue);
1485 chain->queue = NULL;
1487 /* try to set sync to true but it's no biggie when we can't */
1489 gst_play_sink_find_property_sinks (playsink, chain->sink,
1490 "sync", G_TYPE_BOOLEAN)))
1491 g_object_set (elem, "sync", TRUE, NULL);
1494 gst_bin_remove (bin, chain->sink);
1496 GST_WARNING_OBJECT (playsink,
1497 "can't find async property in custom text sink");
1500 if (textsinkpad == NULL) {
1501 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1502 (_("Custom text sink element is not usable.")),
1503 ("fallback to default textoverlay"));
1507 if (textsinkpad == NULL) {
1508 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1509 /* make a little queue */
1510 chain->queue = gst_element_factory_make ("queue", "vqueue");
1511 if (chain->queue == NULL) {
1512 post_missing_element_message (playsink, "queue");
1513 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1514 (_("Missing element '%s' - check your GStreamer installation."),
1515 "queue"), ("video rendering might be suboptimal"));
1517 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1518 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1519 "silent", TRUE, NULL);
1520 gst_bin_add (bin, chain->queue);
1521 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1525 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1526 if (chain->overlay == NULL) {
1527 post_missing_element_message (playsink, "subtitleoverlay");
1528 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1529 (_("Missing element '%s' - check your GStreamer installation."),
1530 "subtitleoverlay"), ("subtitle rendering disabled"));
1532 GstElement *element;
1534 gst_bin_add (bin, chain->overlay);
1536 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1537 if (playsink->font_desc) {
1538 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1539 playsink->font_desc, NULL);
1541 if (playsink->subtitle_encoding) {
1542 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1543 playsink->subtitle_encoding, NULL);
1546 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1547 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1549 /* make another little queue to decouple streams */
1550 element = gst_element_factory_make ("queue", "subqueue");
1551 if (element == NULL) {
1552 post_missing_element_message (playsink, "queue");
1553 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1554 (_("Missing element '%s' - check your GStreamer installation."),
1555 "queue"), ("rendering might be suboptimal"));
1557 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1558 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1559 "silent", TRUE, NULL);
1560 gst_bin_add (bin, element);
1561 gst_element_link_pads_full (element, "src", chain->overlay,
1562 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1563 textsinkpad = gst_element_get_static_pad (element, "sink");
1564 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1570 if (videosinkpad == NULL) {
1571 /* if we still don't have a videosink, we don't have an overlay. the only
1572 * thing we can do is insert an identity and ghost the src
1574 chain->identity = gst_element_factory_make ("identity", "tidentity");
1575 if (chain->identity == NULL) {
1576 post_missing_element_message (playsink, "identity");
1577 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1578 (_("Missing element '%s' - check your GStreamer installation."),
1579 "identity"), (NULL));
1581 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1582 g_object_set (chain->identity, "silent", TRUE, NULL);
1583 gst_bin_add (bin, chain->identity);
1584 srcpad = gst_element_get_static_pad (chain->identity, "src");
1585 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1589 /* expose the ghostpads */
1591 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1592 gst_object_unref (videosinkpad);
1593 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1596 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1597 gst_object_unref (textsinkpad);
1598 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1601 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1602 gst_object_unref (srcpad);
1603 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1610 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1614 g_object_get (object, "volume", &vol, NULL);
1615 playsink->volume = vol;
1617 g_object_notify (G_OBJECT (playsink), "volume");
1621 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1625 g_object_get (object, "mute", &mute, NULL);
1626 playsink->mute = mute;
1628 g_object_notify (G_OBJECT (playsink), "mute");
1631 /* make the chain that contains the elements needed to perform
1634 * We add a tee as the first element so that we can link the visualisation chain
1635 * to it when requested.
1637 * +-------------------------------------------------------------+
1639 * | +---------+ +----------+ +---------+ +---------+ |
1640 * | |audioconv| |audioscale| | volume | |audiosink| |
1641 * | +-srck src-sink src-sink src-sink | |
1642 * | | +---------+ +----------+ +---------+ +---------+ |
1644 * +-------------------------------------------------------------+
1646 static GstPlayAudioChain *
1647 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1649 GstPlayAudioChain *chain;
1651 gboolean have_volume;
1653 GstElement *head, *prev, *elem = NULL;
1655 chain = g_new0 (GstPlayAudioChain, 1);
1656 chain->chain.playsink = playsink;
1657 chain->chain.raw = raw;
1659 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1661 if (playsink->audio_sink) {
1662 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1663 playsink->audio_sink);
1664 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1666 /* only try fallback if no specific sink was chosen */
1667 if (chain->sink == NULL) {
1668 GST_DEBUG_OBJECT (playsink,
1669 "trying pitch ! audioconvert ! autoaudiosink");
1671 gst_parse_bin_from_description
1672 ("pitch ! audioconvert ! autoaudiosink", TRUE, NULL);
1673 chain->sink = try_element (playsink, elem, TRUE);
1675 if (chain->sink == NULL) {
1676 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1677 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1678 chain->sink = try_element (playsink, elem, TRUE);
1680 if (chain->sink == NULL) {
1681 /* if default sink from config.h is different then try it too */
1682 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1683 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1684 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1685 chain->sink = try_element (playsink, elem, TRUE);
1689 playsink->audio_sink = gst_object_ref (chain->sink);
1691 if (chain->sink == NULL)
1694 chain->chain.bin = gst_bin_new ("abin");
1695 bin = GST_BIN_CAST (chain->chain.bin);
1696 gst_object_ref_sink (bin);
1697 gst_bin_add (bin, chain->sink);
1699 /* we have to add a queue when we need to decouple for the video sink in
1700 * visualisations and for streamsynchronizer */
1701 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1702 chain->queue = gst_element_factory_make ("queue", "aqueue");
1703 if (chain->queue == NULL) {
1704 post_missing_element_message (playsink, "queue");
1705 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1706 (_("Missing element '%s' - check your GStreamer installation."),
1707 "queue"), ("audio playback and visualizations might not work"));
1711 g_object_set (chain->queue, "silent", TRUE, NULL);
1712 gst_bin_add (bin, chain->queue);
1713 prev = head = chain->queue;
1716 /* find ts-offset element */
1717 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1718 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1721 /* check if the sink, or something within the sink, has the volume property.
1722 * If it does we don't need to add a volume element. */
1724 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1727 chain->volume = elem;
1729 g_signal_connect (chain->volume, "notify::volume",
1730 G_CALLBACK (notify_volume_cb), playsink);
1732 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1734 chain->sink_volume = TRUE;
1735 /* if the sink also has a mute property we can use this as well. We'll only
1736 * use the mute property if there is a volume property. We can simulate the
1737 * mute with the volume otherwise. */
1739 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1742 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1743 g_signal_connect (chain->mute, "notify::mute",
1744 G_CALLBACK (notify_mute_cb), playsink);
1746 /* use the sink to control the volume and mute */
1747 if (playsink->volume_changed) {
1748 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1749 playsink->volume_changed = FALSE;
1751 if (playsink->mute_changed) {
1753 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1756 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1758 playsink->mute_changed = FALSE;
1761 /* no volume, we need to add a volume element when we can */
1762 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1763 have_volume = FALSE;
1764 chain->sink_volume = FALSE;
1767 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1768 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1769 GST_DEBUG_OBJECT (playsink, "creating audioconvert");
1771 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL);
1772 gst_bin_add (bin, chain->conv);
1774 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1775 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1782 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_converters =
1783 !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1784 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = (!have_volume
1785 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
1787 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1788 GstPlaySinkAudioConvert *conv =
1789 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1792 chain->volume = conv->volume;
1795 g_signal_connect (chain->volume, "notify::volume",
1796 G_CALLBACK (notify_volume_cb), playsink);
1798 /* volume also has the mute property */
1799 chain->mute = chain->volume;
1800 g_signal_connect (chain->mute, "notify::mute",
1801 G_CALLBACK (notify_mute_cb), playsink);
1803 /* configure with the latest volume and mute */
1804 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1806 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1812 /* we only have to link to the previous element if we have something in
1813 * front of the sink */
1814 GST_DEBUG_OBJECT (playsink, "linking to sink");
1815 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1816 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1820 /* post a warning if we have no way to configure the volume */
1822 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1823 (_("No volume control found")), ("Volume/mute is not available"));
1826 /* and ghost the sinkpad of the headmost element */
1827 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1828 pad = gst_element_get_static_pad (head, "sink");
1829 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1830 gst_object_unref (pad);
1831 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1838 if (!elem && !playsink->audio_sink) {
1839 post_missing_element_message (playsink, "autoaudiosink");
1840 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1841 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1842 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1843 (_("Both autoaudiosink and %s elements are missing."),
1844 DEFAULT_AUDIOSINK), (NULL));
1846 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1847 (_("The autoaudiosink element is missing.")), (NULL));
1850 if (playsink->audio_sink) {
1851 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1852 (_("Configured audiosink %s is not working."),
1853 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1854 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1855 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1856 (_("Both autoaudiosink and %s elements are not working."),
1857 DEFAULT_AUDIOSINK), (NULL));
1859 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1860 (_("The autoaudiosink element is not working.")), (NULL));
1863 free_chain ((GstPlayChain *) chain);
1868 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1869 (NULL), ("Failed to configure the audio sink."));
1870 /* checking sink made it READY */
1871 gst_element_set_state (chain->sink, GST_STATE_NULL);
1872 /* Remove chain from the bin to allow reuse later */
1873 gst_bin_remove (bin, chain->sink);
1874 free_chain ((GstPlayChain *) chain);
1880 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1883 GstPlayAudioChain *chain;
1884 GstStateChangeReturn ret;
1886 chain = playsink->audiochain;
1888 chain->chain.raw = raw;
1890 /* if the chain was active we don't do anything */
1891 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1894 /* try to set the sink element to READY again */
1895 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1896 if (ret == GST_STATE_CHANGE_FAILURE)
1899 /* find ts-offset element */
1900 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1901 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1904 /* check if the sink, or something within the sink, has the volume property.
1905 * If it does we don't need to add a volume element. */
1907 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1910 chain->volume = elem;
1912 if (playsink->volume_changed) {
1913 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1915 /* use the sink to control the volume */
1916 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1917 playsink->volume_changed = FALSE;
1920 g_signal_connect (chain->volume, "notify::volume",
1921 G_CALLBACK (notify_volume_cb), playsink);
1922 /* if the sink also has a mute property we can use this as well. We'll only
1923 * use the mute property if there is a volume property. We can simulate the
1924 * mute with the volume otherwise. */
1926 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1929 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1930 g_signal_connect (chain->mute, "notify::mute",
1931 G_CALLBACK (notify_mute_cb), playsink);
1934 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE;
1936 GstPlaySinkAudioConvert *conv =
1937 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1939 /* no volume, we need to add a volume element when we can */
1940 conv->use_volume = TRUE;
1941 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1943 /* Disconnect signals */
1944 disconnect_chain (chain, playsink);
1947 chain->volume = conv->volume;
1948 chain->mute = chain->volume;
1950 g_signal_connect (chain->volume, "notify::volume",
1951 G_CALLBACK (notify_volume_cb), playsink);
1953 g_signal_connect (chain->mute, "notify::mute",
1954 G_CALLBACK (notify_mute_cb), playsink);
1956 /* configure with the latest volume and mute */
1957 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1958 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1961 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
1967 * +-------------------------------------------------------------------+
1969 * | +----------+ +------------+ +----------+ +-------+ |
1970 * | | visqueue | | audioconv | | audiores | | vis | |
1971 * | +-sink src-sink + samp src-sink src-sink src-+ |
1972 * | | +----------+ +------------+ +----------+ +-------+ | |
1974 * +-------------------------------------------------------------------+
1977 static GstPlayVisChain *
1978 gen_vis_chain (GstPlaySink * playsink)
1980 GstPlayVisChain *chain;
1986 chain = g_new0 (GstPlayVisChain, 1);
1987 chain->chain.playsink = playsink;
1989 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
1991 chain->chain.bin = gst_bin_new ("visbin");
1992 bin = GST_BIN_CAST (chain->chain.bin);
1993 gst_object_ref_sink (bin);
1995 /* we're queuing raw audio here, we can remove this queue when we can disable
1996 * async behaviour in the video sink. */
1997 chain->queue = gst_element_factory_make ("queue", "visqueue");
1998 if (chain->queue == NULL)
2000 g_object_set (chain->queue, "silent", TRUE, NULL);
2001 gst_bin_add (bin, chain->queue);
2003 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2004 if (chain->conv == NULL)
2005 goto no_audioconvert;
2006 gst_bin_add (bin, chain->conv);
2008 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2009 if (chain->resample == NULL)
2010 goto no_audioresample;
2011 gst_bin_add (bin, chain->resample);
2013 /* this pad will be used for blocking the dataflow and switching the vis
2015 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2017 if (playsink->visualisation) {
2018 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2019 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2021 if (chain->vis == NULL) {
2022 GST_DEBUG_OBJECT (playsink, "trying goom");
2023 elem = gst_element_factory_make ("goom", "vis");
2024 chain->vis = try_element (playsink, elem, TRUE);
2026 if (chain->vis == NULL)
2029 gst_bin_add (bin, chain->vis);
2031 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2032 GST_PAD_LINK_CHECK_NOTHING);
2034 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2035 GST_PAD_LINK_CHECK_NOTHING);
2037 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2038 GST_PAD_LINK_CHECK_NOTHING);
2042 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2043 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2045 pad = gst_element_get_static_pad (chain->queue, "sink");
2046 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2047 gst_object_unref (pad);
2048 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2050 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2051 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2058 post_missing_element_message (playsink, "queue");
2059 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2060 (_("Missing element '%s' - check your GStreamer installation."),
2062 free_chain ((GstPlayChain *) chain);
2067 post_missing_element_message (playsink, "audioconvert");
2068 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2069 (_("Missing element '%s' - check your GStreamer installation."),
2070 "audioconvert"), ("possibly a liboil version mismatch?"));
2071 free_chain ((GstPlayChain *) chain);
2076 post_missing_element_message (playsink, "audioresample");
2077 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2078 (_("Missing element '%s' - check your GStreamer installation."),
2079 "audioresample"), (NULL));
2080 free_chain ((GstPlayChain *) chain);
2085 post_missing_element_message (playsink, "goom");
2086 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2087 (_("Missing element '%s' - check your GStreamer installation."),
2089 free_chain ((GstPlayChain *) chain);
2094 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2095 (NULL), ("Failed to configure the visualisation element."));
2096 /* element made it to READY */
2097 gst_element_set_state (chain->vis, GST_STATE_NULL);
2098 free_chain ((GstPlayChain *) chain);
2103 /* this function is called when all the request pads are requested and when we
2104 * have to construct the final pipeline. Based on the flags we construct the
2105 * final output pipelines.
2108 gst_play_sink_reconfigure (GstPlaySink * playsink)
2111 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2113 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2115 /* assume we need nothing */
2116 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2118 GST_PLAY_SINK_LOCK (playsink);
2119 GST_OBJECT_LOCK (playsink);
2120 /* get flags, there are protected with the object lock */
2121 flags = playsink->flags;
2122 GST_OBJECT_UNLOCK (playsink);
2124 /* figure out which components we need */
2125 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2126 /* we have subtitles and we are requested to show it */
2130 if (((flags & GST_PLAY_FLAG_VIDEO)
2131 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2132 /* we have video and we are requested to show it */
2135 /* we only deinterlace if native video is not requested and
2136 * we have raw video */
2137 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2138 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2139 need_deinterlace = TRUE;
2142 if (playsink->audio_pad) {
2143 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2146 if (playsink->audio_pad_raw) {
2147 /* only can do vis with raw uncompressed audio */
2148 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2149 /* also add video when we add visualisation */
2156 /* we have a text_pad and we need text rendering, in this case we need a
2157 * video_pad to combine the video with the text or visualizations */
2158 if (need_text && !need_video) {
2159 if (playsink->video_pad) {
2161 } else if (need_audio) {
2162 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2163 (_("Can't play a text file without video or visualizations.")),
2164 ("Have text pad but no video pad or visualizations"));
2167 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2168 (_("Can't play a text file without video or visualizations.")),
2169 ("Have text pad but no video pad or visualizations"));
2170 GST_PLAY_SINK_UNLOCK (playsink);
2175 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2176 need_video, need_vis, need_text);
2178 /* set up video pipeline */
2180 gboolean raw, async;
2182 /* we need a raw sink when we do vis or when we have a raw pad */
2183 raw = need_vis ? TRUE : playsink->video_pad_raw;
2184 /* we try to set the sink async=FALSE when we need vis, this way we can
2185 * avoid a queue in the audio chain. */
2188 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2189 playsink->video_pad_raw);
2191 if (playsink->videochain) {
2192 /* try to reactivate the chain */
2193 if (!setup_video_chain (playsink, raw, async)) {
2194 if (playsink->video_sinkpad_stream_synchronizer) {
2195 gst_element_release_request_pad (GST_ELEMENT_CAST
2196 (playsink->stream_synchronizer),
2197 playsink->video_sinkpad_stream_synchronizer);
2198 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2199 playsink->video_sinkpad_stream_synchronizer = NULL;
2200 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2201 playsink->video_srcpad_stream_synchronizer = NULL;
2204 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2206 /* Remove the sink from the bin to keep its state
2207 * and unparent it to allow reuse */
2208 if (playsink->videochain->sink)
2209 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2210 playsink->videochain->sink);
2212 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2213 free_chain ((GstPlayChain *) playsink->videochain);
2214 playsink->videochain = NULL;
2218 if (!playsink->videochain)
2219 playsink->videochain = gen_video_chain (playsink, raw, async);
2220 if (!playsink->videochain)
2223 if (!playsink->video_sinkpad_stream_synchronizer) {
2226 playsink->video_sinkpad_stream_synchronizer =
2227 gst_element_get_request_pad (GST_ELEMENT_CAST
2228 (playsink->stream_synchronizer), "sink_%d");
2229 it = gst_pad_iterate_internal_links
2230 (playsink->video_sinkpad_stream_synchronizer);
2232 gst_iterator_next (it,
2233 (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2234 g_assert (playsink->video_srcpad_stream_synchronizer);
2235 gst_iterator_free (it);
2238 if (playsink->video_pad)
2239 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2240 playsink->video_sinkpad_stream_synchronizer);
2242 if (need_deinterlace) {
2243 if (!playsink->videodeinterlacechain)
2244 playsink->videodeinterlacechain =
2245 gen_video_deinterlace_chain (playsink);
2246 if (!playsink->videodeinterlacechain)
2249 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2251 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2253 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2254 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2256 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2257 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2259 if (playsink->videodeinterlacechain) {
2260 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2261 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2266 GST_DEBUG_OBJECT (playsink, "adding video chain");
2267 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2268 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2269 /* if we are not part of vis or subtitles, set the ghostpad target */
2270 if (!need_vis && !need_text && (!playsink->textchain
2271 || !playsink->text_pad)) {
2272 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2273 if (need_deinterlace)
2274 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2275 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2277 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2278 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2281 GST_DEBUG_OBJECT (playsink, "no video needed");
2282 if (playsink->videochain) {
2283 GST_DEBUG_OBJECT (playsink, "removing video chain");
2284 if (playsink->vischain) {
2287 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2289 /* also had visualisation, release the tee srcpad before we then
2290 * unlink the video from it */
2291 if (playsink->audio_tee_vissrc) {
2292 gst_element_release_request_pad (playsink->audio_tee,
2293 playsink->audio_tee_vissrc);
2294 gst_object_unref (playsink->audio_tee_vissrc);
2295 playsink->audio_tee_vissrc = NULL;
2298 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2299 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2302 if (playsink->video_sinkpad_stream_synchronizer) {
2303 gst_element_release_request_pad (GST_ELEMENT_CAST
2304 (playsink->stream_synchronizer),
2305 playsink->video_sinkpad_stream_synchronizer);
2306 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2307 playsink->video_sinkpad_stream_synchronizer = NULL;
2308 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2309 playsink->video_srcpad_stream_synchronizer = NULL;
2312 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2313 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2314 g_object_unref (playsink->videochain->ts_offset);
2315 playsink->videochain->ts_offset = NULL;
2318 if (playsink->videodeinterlacechain) {
2319 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2320 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2323 if (playsink->video_pad)
2324 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2330 GST_DEBUG_OBJECT (playsink, "adding audio");
2332 /* get a raw sink if we are asked for a raw pad */
2333 raw = playsink->audio_pad_raw;
2335 if (playsink->audiochain) {
2336 /* try to reactivate the chain */
2337 if (!setup_audio_chain (playsink, raw)) {
2338 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2339 if (playsink->audio_tee_asrc) {
2340 gst_element_release_request_pad (playsink->audio_tee,
2341 playsink->audio_tee_asrc);
2342 gst_object_unref (playsink->audio_tee_asrc);
2343 playsink->audio_tee_asrc = NULL;
2346 if (playsink->audio_sinkpad_stream_synchronizer) {
2347 gst_element_release_request_pad (GST_ELEMENT_CAST
2348 (playsink->stream_synchronizer),
2349 playsink->audio_sinkpad_stream_synchronizer);
2350 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2351 playsink->audio_sinkpad_stream_synchronizer = NULL;
2352 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2353 playsink->audio_srcpad_stream_synchronizer = NULL;
2356 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2358 /* Remove the sink from the bin to keep its state
2359 * and unparent it to allow reuse */
2360 if (playsink->audiochain->sink)
2361 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2362 playsink->audiochain->sink);
2364 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2365 disconnect_chain (playsink->audiochain, playsink);
2366 playsink->audiochain->volume = NULL;
2367 playsink->audiochain->mute = NULL;
2368 g_object_unref (playsink->audiochain->ts_offset);
2369 playsink->audiochain->ts_offset = NULL;
2370 free_chain ((GstPlayChain *) playsink->audiochain);
2371 playsink->audiochain = NULL;
2372 playsink->volume_changed = playsink->mute_changed = FALSE;
2376 if (!playsink->audiochain) {
2377 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2378 playsink->audiochain = gen_audio_chain (playsink, raw);
2381 if (!playsink->audio_sinkpad_stream_synchronizer) {
2384 playsink->audio_sinkpad_stream_synchronizer =
2385 gst_element_get_request_pad (GST_ELEMENT_CAST
2386 (playsink->stream_synchronizer), "sink_%d");
2387 it = gst_pad_iterate_internal_links
2388 (playsink->audio_sinkpad_stream_synchronizer);
2390 gst_iterator_next (it,
2391 (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2392 g_assert (playsink->audio_srcpad_stream_synchronizer);
2393 gst_iterator_free (it);
2396 if (playsink->audiochain) {
2397 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2398 if (playsink->audio_tee_asrc == NULL) {
2399 playsink->audio_tee_asrc =
2400 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2402 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2403 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2404 gst_pad_link_full (playsink->audio_tee_asrc,
2405 playsink->audio_sinkpad_stream_synchronizer,
2406 GST_PAD_LINK_CHECK_NOTHING);
2407 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2408 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2411 GST_DEBUG_OBJECT (playsink, "no audio needed");
2412 /* we have no audio or we are requested to not play audio */
2413 if (playsink->audiochain) {
2414 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2415 /* release the audio pad */
2416 if (playsink->audio_tee_asrc) {
2417 gst_element_release_request_pad (playsink->audio_tee,
2418 playsink->audio_tee_asrc);
2419 gst_object_unref (playsink->audio_tee_asrc);
2420 playsink->audio_tee_asrc = NULL;
2423 if (playsink->audio_sinkpad_stream_synchronizer) {
2424 gst_element_release_request_pad (GST_ELEMENT_CAST
2425 (playsink->stream_synchronizer),
2426 playsink->audio_sinkpad_stream_synchronizer);
2427 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2428 playsink->audio_sinkpad_stream_synchronizer = NULL;
2429 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2430 playsink->audio_srcpad_stream_synchronizer = NULL;
2433 if (playsink->audiochain->sink_volume) {
2434 disconnect_chain (playsink->audiochain, playsink);
2435 playsink->audiochain->volume = NULL;
2436 playsink->audiochain->mute = NULL;
2437 g_object_unref (playsink->audiochain->ts_offset);
2438 playsink->audiochain->ts_offset = NULL;
2440 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2441 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2448 if (!playsink->vischain)
2449 playsink->vischain = gen_vis_chain (playsink);
2451 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2453 if (playsink->vischain) {
2454 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2456 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2457 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2458 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2459 if (playsink->audio_tee_vissrc == NULL) {
2460 playsink->audio_tee_vissrc =
2461 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2463 gst_pad_link_full (playsink->audio_tee_vissrc,
2464 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2465 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2466 GST_PAD_LINK_CHECK_NOTHING);
2467 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2468 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2469 gst_object_unref (srcpad);
2472 GST_DEBUG_OBJECT (playsink, "no vis needed");
2473 if (playsink->vischain) {
2474 if (playsink->audio_tee_vissrc) {
2475 gst_element_release_request_pad (playsink->audio_tee,
2476 playsink->audio_tee_vissrc);
2477 gst_object_unref (playsink->audio_tee_vissrc);
2478 playsink->audio_tee_vissrc = NULL;
2480 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2481 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2482 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2487 GST_DEBUG_OBJECT (playsink, "adding text");
2488 if (!playsink->textchain) {
2489 GST_DEBUG_OBJECT (playsink, "creating text chain");
2490 playsink->textchain = gen_text_chain (playsink);
2492 if (playsink->textchain) {
2495 GST_DEBUG_OBJECT (playsink, "adding text chain");
2496 if (playsink->textchain->overlay)
2497 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2499 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2501 if (!playsink->text_sinkpad_stream_synchronizer) {
2502 playsink->text_sinkpad_stream_synchronizer =
2503 gst_element_get_request_pad (GST_ELEMENT_CAST
2504 (playsink->stream_synchronizer), "sink_%d");
2505 it = gst_pad_iterate_internal_links
2506 (playsink->text_sinkpad_stream_synchronizer);
2508 gst_iterator_next (it,
2509 (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2510 g_assert (playsink->text_srcpad_stream_synchronizer);
2511 gst_iterator_free (it);
2513 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2514 playsink->text_sinkpad_stream_synchronizer);
2515 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2516 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2523 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2524 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2525 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2526 GST_PAD_LINK_CHECK_NOTHING);
2527 gst_object_unref (srcpad);
2529 if (need_deinterlace)
2530 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2531 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2533 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2534 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2536 gst_pad_link_full (playsink->textchain->srcpad,
2537 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2539 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2542 GST_DEBUG_OBJECT (playsink, "no text needed");
2543 /* we have no subtitles/text or we are requested to not show them */
2545 if (playsink->text_sinkpad_stream_synchronizer) {
2546 gst_element_release_request_pad (GST_ELEMENT_CAST
2547 (playsink->stream_synchronizer),
2548 playsink->text_sinkpad_stream_synchronizer);
2549 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2550 playsink->text_sinkpad_stream_synchronizer = NULL;
2551 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2552 playsink->text_srcpad_stream_synchronizer = NULL;
2555 if (playsink->textchain) {
2556 if (playsink->text_pad == NULL) {
2557 /* no text pad, remove the chain entirely */
2558 GST_DEBUG_OBJECT (playsink, "removing text chain");
2559 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2560 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2562 /* we have a chain and a textpad, turn the subtitles off */
2563 GST_DEBUG_OBJECT (playsink, "turning off the text");
2564 if (playsink->textchain->overlay)
2565 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2569 if (!need_video && playsink->video_pad) {
2570 if (playsink->video_sinkpad_stream_synchronizer) {
2571 gst_element_release_request_pad (GST_ELEMENT_CAST
2572 (playsink->stream_synchronizer),
2573 playsink->video_sinkpad_stream_synchronizer);
2574 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2575 playsink->video_sinkpad_stream_synchronizer = NULL;
2576 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2577 playsink->video_srcpad_stream_synchronizer = NULL;
2580 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2583 if (playsink->text_pad && !playsink->textchain)
2584 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2586 update_av_offset (playsink);
2587 do_async_done (playsink);
2588 GST_PLAY_SINK_UNLOCK (playsink);
2595 /* gen_ chain already posted error */
2596 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2597 GST_PLAY_SINK_UNLOCK (playsink);
2603 * gst_play_sink_set_flags:
2604 * @playsink: a #GstPlaySink
2605 * @flags: #GstPlayFlags
2607 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2608 * when constructing the sink pipelins.
2610 * Returns: TRUE if the flags could be configured.
2613 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2615 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2617 GST_OBJECT_LOCK (playsink);
2618 playsink->flags = flags;
2619 GST_OBJECT_UNLOCK (playsink);
2625 * gst_play_sink_get_flags:
2626 * @playsink: a #GstPlaySink
2628 * Get the flags of @playsink. That flags control the behaviour of the sink when
2629 * it constructs the sink pipelines.
2631 * Returns: the currently configured #GstPlayFlags.
2634 gst_play_sink_get_flags (GstPlaySink * playsink)
2638 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2640 GST_OBJECT_LOCK (playsink);
2641 res = playsink->flags;
2642 GST_OBJECT_UNLOCK (playsink);
2648 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2650 GstPlayTextChain *chain;
2652 GST_PLAY_SINK_LOCK (playsink);
2653 chain = (GstPlayTextChain *) playsink->textchain;
2654 g_free (playsink->font_desc);
2655 playsink->font_desc = g_strdup (desc);
2656 if (chain && chain->overlay) {
2657 g_object_set (chain->overlay, "font-desc", desc, NULL);
2659 GST_PLAY_SINK_UNLOCK (playsink);
2663 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2665 gchar *result = NULL;
2666 GstPlayTextChain *chain;
2668 GST_PLAY_SINK_LOCK (playsink);
2669 chain = (GstPlayTextChain *) playsink->textchain;
2670 if (chain && chain->overlay) {
2671 g_object_get (chain->overlay, "font-desc", &result, NULL);
2672 playsink->font_desc = g_strdup (result);
2674 result = g_strdup (playsink->font_desc);
2676 GST_PLAY_SINK_UNLOCK (playsink);
2682 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2683 const gchar * encoding)
2685 GstPlayTextChain *chain;
2687 GST_PLAY_SINK_LOCK (playsink);
2688 chain = (GstPlayTextChain *) playsink->textchain;
2689 g_free (playsink->subtitle_encoding);
2690 playsink->subtitle_encoding = g_strdup (encoding);
2691 if (chain && chain->overlay) {
2692 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2694 GST_PLAY_SINK_UNLOCK (playsink);
2698 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2700 gchar *result = NULL;
2701 GstPlayTextChain *chain;
2703 GST_PLAY_SINK_LOCK (playsink);
2704 chain = (GstPlayTextChain *) playsink->textchain;
2705 if (chain && chain->overlay) {
2706 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2707 playsink->subtitle_encoding = g_strdup (result);
2709 result = g_strdup (playsink->subtitle_encoding);
2711 GST_PLAY_SINK_UNLOCK (playsink);
2717 update_av_offset (GstPlaySink * playsink)
2720 GstPlayAudioChain *achain;
2721 GstPlayVideoChain *vchain;
2723 av_offset = playsink->av_offset;
2724 achain = (GstPlayAudioChain *) playsink->audiochain;
2725 vchain = (GstPlayVideoChain *) playsink->videochain;
2727 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2728 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2729 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2731 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2736 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2738 GST_PLAY_SINK_LOCK (playsink);
2739 playsink->av_offset = av_offset;
2740 update_av_offset (playsink);
2741 GST_PLAY_SINK_UNLOCK (playsink);
2745 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2749 GST_PLAY_SINK_LOCK (playsink);
2750 result = playsink->av_offset;
2751 GST_PLAY_SINK_UNLOCK (playsink);
2757 * gst_play_sink_get_last_frame:
2758 * @playsink: a #GstPlaySink
2760 * Get the last displayed frame from @playsink. This frame is in the native
2761 * format of the sink element, the caps on the result buffer contain the format
2762 * of the frame data.
2764 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2768 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2770 GstBuffer *result = NULL;
2771 GstPlayVideoChain *chain;
2773 GST_PLAY_SINK_LOCK (playsink);
2774 GST_DEBUG_OBJECT (playsink, "taking last frame");
2775 /* get the video chain if we can */
2776 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2777 GST_DEBUG_OBJECT (playsink, "found video chain");
2778 /* see if the chain is active */
2779 if (chain->chain.activated && chain->sink) {
2782 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2784 /* find and get the last-buffer property now */
2786 gst_play_sink_find_property (playsink, chain->sink,
2787 "last-buffer", GST_TYPE_BUFFER))) {
2788 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2789 g_object_get (elem, "last-buffer", &result, NULL);
2790 gst_object_unref (elem);
2794 GST_PLAY_SINK_UNLOCK (playsink);
2800 * gst_play_sink_convert_frame:
2801 * @playsink: a #GstPlaySink
2804 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2805 * be in the native format of the sink element and the caps on the buffer
2806 * describe the format of the frame. If @caps is not %NULL, the video
2807 * frame will be converted to the format of the caps.
2809 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2810 * available or when the conversion failed.
2813 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2817 result = gst_play_sink_get_last_frame (playsink);
2818 if (result != NULL && caps != NULL) {
2822 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2823 gst_buffer_unref (result);
2824 if (temp == NULL && err) {
2825 /* I'm really uncertain whether we should make playsink post an error
2826 * on the bus or not. It's not like it's a critical issue regarding
2827 * playsink behaviour. */
2828 GST_ERROR ("Error converting frame: %s", err->message);
2836 is_raw_structure (GstStructure * s)
2840 name = gst_structure_get_name (s);
2842 if (g_str_has_prefix (name, "video/x-raw-") ||
2843 g_str_has_prefix (name, "audio/x-raw-"))
2849 is_raw_pad (GstPad * pad)
2851 GstPad *peer = gst_pad_get_peer (pad);
2853 gboolean raw = TRUE;
2858 caps = gst_pad_get_negotiated_caps (peer);
2862 caps = gst_pad_get_caps_reffed (peer);
2864 n = gst_caps_get_size (caps);
2865 for (i = 0; i < n; i++) {
2866 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2870 } else if (raw != r) {
2871 GST_ERROR_OBJECT (pad,
2872 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2878 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2880 gst_caps_unref (caps);
2881 gst_object_unref (peer);
2887 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
2889 GstPlaySink *playsink = (GstPlaySink *) user_data;
2892 GST_PLAY_SINK_LOCK (playsink);
2894 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2895 if (pad == playsink->video_pad) {
2896 playsink->video_pad_blocked = blocked;
2897 GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
2899 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2900 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2902 } else if (pad == playsink->audio_pad) {
2903 playsink->audio_pad_blocked = blocked;
2904 GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
2906 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2907 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2909 } else if (pad == playsink->text_pad) {
2910 playsink->text_pad_blocked = blocked;
2911 GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
2913 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
2917 gst_object_unref (pad);
2918 GST_PLAY_SINK_UNLOCK (playsink);
2922 /* We reconfigure when for ALL streams:
2923 * * there isn't a pad
2924 * * OR the pad is blocked
2925 * * OR there are no pending blocks on that pad
2928 if ((!playsink->video_pad || playsink->video_pad_blocked
2929 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
2930 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
2931 && (!playsink->text_pad || playsink->text_pad_blocked
2932 || !PENDING_TEXT_BLOCK (playsink))) {
2933 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
2935 if (playsink->video_pad) {
2936 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
2937 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
2938 playsink->video_pad_raw);
2941 if (playsink->audio_pad) {
2942 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
2943 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
2944 playsink->audio_pad_raw);
2947 gst_play_sink_reconfigure (playsink);
2949 if (playsink->video_pad) {
2951 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2952 (playsink->video_pad)));
2953 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
2954 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2955 gst_object_unref (opad);
2958 if (playsink->audio_pad) {
2960 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2961 (playsink->audio_pad)));
2962 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
2963 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2964 gst_object_unref (opad);
2967 if (playsink->text_pad) {
2969 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2970 (playsink->text_pad)));
2971 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
2972 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2973 gst_object_unref (opad);
2977 gst_object_unref (pad);
2979 GST_PLAY_SINK_UNLOCK (playsink);
2983 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
2985 gboolean reconfigure = FALSE;
2989 g_object_get (pad, "caps", &caps, NULL);
2993 if (pad == playsink->audio_pad) {
2994 raw = is_raw_pad (pad);
2995 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
2996 && playsink->audiochain;
2997 GST_DEBUG_OBJECT (pad,
2998 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3000 } else if (pad == playsink->video_pad) {
3001 raw = is_raw_pad (pad);
3002 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3003 && playsink->videochain;
3004 GST_DEBUG_OBJECT (pad,
3005 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3009 gst_caps_unref (caps);
3012 GST_PLAY_SINK_LOCK (playsink);
3013 if (playsink->video_pad) {
3015 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3016 (playsink->video_pad)));
3017 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3018 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3019 gst_object_unref (opad);
3022 if (playsink->audio_pad) {
3024 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3025 (playsink->audio_pad)));
3026 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3027 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3028 gst_object_unref (opad);
3031 if (playsink->text_pad) {
3033 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3034 (playsink->text_pad)));
3035 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3036 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3037 gst_object_unref (opad);
3039 GST_PLAY_SINK_UNLOCK (playsink);
3044 * gst_play_sink_request_pad
3045 * @playsink: a #GstPlaySink
3046 * @type: a #GstPlaySinkType
3048 * Create or return a pad of @type.
3050 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3053 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3056 gboolean created = FALSE;
3057 gboolean activate = TRUE;
3058 const gchar *pad_name = NULL;
3060 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3062 GST_PLAY_SINK_LOCK (playsink);
3064 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3065 case GST_PLAY_SINK_TYPE_AUDIO:
3066 pad_name = "audio_sink";
3067 if (!playsink->audio_tee) {
3068 GST_LOG_OBJECT (playsink, "creating tee");
3069 /* create tee when needed. This element will feed the audio sink chain
3070 * and the vis chain. */
3071 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3072 if (playsink->audio_tee == NULL) {
3073 post_missing_element_message (playsink, "tee");
3074 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3075 (_("Missing element '%s' - check your GStreamer installation."),
3080 playsink->audio_tee_sink =
3081 gst_element_get_static_pad (playsink->audio_tee, "sink");
3082 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3083 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3086 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3088 if (!playsink->audio_pad) {
3089 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3090 playsink->audio_pad =
3091 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3092 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3093 G_CALLBACK (caps_notify_cb), playsink);
3096 playsink->audio_pad_raw = FALSE;
3097 res = playsink->audio_pad;
3099 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3100 case GST_PLAY_SINK_TYPE_VIDEO:
3101 pad_name = "video_sink";
3102 if (!playsink->video_pad) {
3103 GST_LOG_OBJECT (playsink, "ghosting videosink");
3104 playsink->video_pad =
3105 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3106 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3107 G_CALLBACK (caps_notify_cb), playsink);
3110 playsink->video_pad_raw = FALSE;
3111 res = playsink->video_pad;
3113 case GST_PLAY_SINK_TYPE_TEXT:
3114 GST_LOG_OBJECT (playsink, "ghosting text");
3115 if (!playsink->text_pad) {
3116 playsink->text_pad =
3117 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3120 res = playsink->text_pad;
3122 case GST_PLAY_SINK_TYPE_FLUSHING:
3126 /* we need a unique padname for the flushing pad. */
3127 padname = g_strdup_printf ("flushing_%d", playsink->count);
3128 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3139 GST_PLAY_SINK_UNLOCK (playsink);
3141 if (created && res) {
3142 /* we have to add the pad when it's active or we get an error when the
3143 * element is 'running' */
3144 gst_pad_set_active (res, TRUE);
3145 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3146 if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3148 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3150 gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3151 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3152 PENDING_FLAG_SET (playsink, type);
3153 gst_object_unref (blockpad);
3156 gst_pad_set_active (res, activate);
3163 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3168 GstPlaySinkType type;
3169 const gchar *tplname;
3171 g_return_val_if_fail (templ != NULL, NULL);
3173 GST_DEBUG_OBJECT (element, "name:%s", name);
3175 psink = GST_PLAY_SINK (element);
3176 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3178 /* Figure out the GstPlaySinkType based on the template */
3179 if (!strcmp (tplname, "audio_sink"))
3180 type = GST_PLAY_SINK_TYPE_AUDIO;
3181 else if (!strcmp (tplname, "audio_raw_sink"))
3182 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3183 else if (!strcmp (tplname, "video_sink"))
3184 type = GST_PLAY_SINK_TYPE_VIDEO;
3185 else if (!strcmp (tplname, "video_raw_sink"))
3186 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3187 else if (!strcmp (tplname, "text_sink"))
3188 type = GST_PLAY_SINK_TYPE_TEXT;
3190 goto unknown_template;
3192 pad = gst_play_sink_request_pad (psink, type);
3196 GST_WARNING_OBJECT (element, "Unknown pad template");
3201 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3203 GstPad **res = NULL;
3204 gboolean untarget = TRUE;
3206 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3208 GST_PLAY_SINK_LOCK (playsink);
3209 if (pad == playsink->video_pad) {
3210 res = &playsink->video_pad;
3211 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3213 } else if (pad == playsink->audio_pad) {
3214 res = &playsink->audio_pad;
3215 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3217 } else if (pad == playsink->text_pad) {
3218 res = &playsink->text_pad;
3220 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3224 GST_PLAY_SINK_UNLOCK (playsink);
3227 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3228 gst_pad_set_active (*res, FALSE);
3230 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3231 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3233 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3234 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3240 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3242 GstPlaySink *psink = GST_PLAY_SINK (element);
3244 gst_play_sink_release_pad (psink, pad);
3248 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3250 GstPlaySink *playsink;
3252 playsink = GST_PLAY_SINK_CAST (bin);
3254 switch (GST_MESSAGE_TYPE (message)) {
3255 case GST_MESSAGE_STEP_DONE:
3260 gboolean flush, intermediate, eos;
3263 GST_INFO_OBJECT (playsink, "Handling step-done message");
3264 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3265 &intermediate, &duration, &eos);
3267 if (format == GST_FORMAT_BUFFERS) {
3268 /* for the buffer format, we align the other streams */
3269 if (playsink->audiochain) {
3273 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3276 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3277 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3281 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3285 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3290 /* Send an event to our sinks until one of them works; don't then send to the
3291 * remaining sinks (unlike GstBin)
3292 * Special case: If a text sink is set we need to send the event
3293 * to them in case it's source is different from the a/v stream's source.
3296 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3298 gboolean res = TRUE;
3300 if (playsink->textchain && playsink->textchain->sink) {
3301 gst_event_ref (event);
3302 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3303 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to text sink");
3305 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3309 if (playsink->videochain) {
3310 gst_event_ref (event);
3311 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3312 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
3315 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3317 if (playsink->audiochain) {
3318 gst_event_ref (event);
3319 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3320 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
3323 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3327 gst_event_unref (event);
3331 /* We only want to send the event to a single sink (overriding GstBin's
3332 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3333 * events appropriately. So, this is a messy duplication of code. */
3335 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3337 gboolean res = FALSE;
3338 GstEventType event_type = GST_EVENT_TYPE (event);
3339 GstPlaySink *playsink;
3341 playsink = GST_PLAY_SINK_CAST (element);
3343 switch (event_type) {
3344 case GST_EVENT_SEEK:
3345 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3346 res = gst_play_sink_send_event_to_sink (playsink, event);
3348 case GST_EVENT_STEP:
3353 gboolean flush, intermediate;
3355 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3358 if (format == GST_FORMAT_BUFFERS) {
3359 /* for buffers, we will try to step video frames, for other formats we
3360 * send the step to all sinks */
3361 res = gst_play_sink_send_event_to_sink (playsink, event);
3364 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3371 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3378 static GstStateChangeReturn
3379 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3381 GstStateChangeReturn ret;
3382 GstStateChangeReturn bret;
3384 GstPlaySink *playsink;
3386 playsink = GST_PLAY_SINK (element);
3388 switch (transition) {
3389 case GST_STATE_CHANGE_READY_TO_PAUSED:
3390 playsink->need_async_start = TRUE;
3391 /* we want to go async to PAUSED until we managed to configure and add the
3393 do_async_start (playsink);
3394 ret = GST_STATE_CHANGE_ASYNC;
3396 case GST_STATE_CHANGE_PAUSED_TO_READY:
3397 /* unblock all pads here */
3398 GST_PLAY_SINK_LOCK (playsink);
3399 if (playsink->video_pad) {
3401 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3402 (playsink->video_pad)));
3403 if (gst_pad_is_blocked (opad)) {
3404 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3405 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3407 gst_object_unref (opad);
3408 playsink->video_pad_blocked = FALSE;
3411 if (playsink->audio_pad) {
3413 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3414 (playsink->audio_pad)));
3416 if (gst_pad_is_blocked (opad)) {
3417 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3418 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3420 gst_object_unref (opad);
3421 playsink->audio_pad_blocked = FALSE;
3424 if (playsink->text_pad) {
3426 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3427 (playsink->text_pad)));
3428 if (gst_pad_is_blocked (opad)) {
3429 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3430 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3432 gst_object_unref (opad);
3433 playsink->text_pad_blocked = FALSE;
3435 GST_PLAY_SINK_UNLOCK (playsink);
3437 case GST_STATE_CHANGE_READY_TO_NULL:
3438 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3439 /* remove our links to the mute and volume elements when they were
3440 * provided by a sink */
3441 disconnect_chain (playsink->audiochain, playsink);
3442 playsink->audiochain->volume = NULL;
3443 playsink->audiochain->mute = NULL;
3444 g_object_unref (playsink->audiochain->ts_offset);
3445 playsink->audiochain->ts_offset = NULL;
3447 ret = GST_STATE_CHANGE_SUCCESS;
3450 /* all other state changes return SUCCESS by default, this value can be
3451 * overridden by the result of the children */
3452 ret = GST_STATE_CHANGE_SUCCESS;
3456 /* do the state change of the children */
3458 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3460 /* now look at the result of our children and adjust the return value */
3462 case GST_STATE_CHANGE_FAILURE:
3463 /* failure, we stop */
3464 goto activate_failed;
3465 case GST_STATE_CHANGE_NO_PREROLL:
3466 /* some child returned NO_PREROLL. This is strange but we never know. We
3467 * commit our async state change (if any) and return the NO_PREROLL */
3468 do_async_done (playsink);
3471 case GST_STATE_CHANGE_ASYNC:
3472 /* some child was async, return this */
3476 /* return our previously configured return value */
3480 switch (transition) {
3481 case GST_STATE_CHANGE_READY_TO_PAUSED:
3483 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3484 /* FIXME Release audio device when we implement that */
3485 playsink->need_async_start = TRUE;
3487 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3488 if (playsink->video_sinkpad_stream_synchronizer) {
3489 gst_element_release_request_pad (GST_ELEMENT_CAST
3490 (playsink->stream_synchronizer),
3491 playsink->video_sinkpad_stream_synchronizer);
3492 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3493 playsink->video_sinkpad_stream_synchronizer = NULL;
3494 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3495 playsink->video_srcpad_stream_synchronizer = NULL;
3497 if (playsink->audio_sinkpad_stream_synchronizer) {
3498 gst_element_release_request_pad (GST_ELEMENT_CAST
3499 (playsink->stream_synchronizer),
3500 playsink->audio_sinkpad_stream_synchronizer);
3501 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3502 playsink->audio_sinkpad_stream_synchronizer = NULL;
3503 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3504 playsink->audio_srcpad_stream_synchronizer = NULL;
3506 if (playsink->text_sinkpad_stream_synchronizer) {
3507 gst_element_release_request_pad (GST_ELEMENT_CAST
3508 (playsink->stream_synchronizer),
3509 playsink->text_sinkpad_stream_synchronizer);
3510 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3511 playsink->text_sinkpad_stream_synchronizer = NULL;
3512 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3513 playsink->text_srcpad_stream_synchronizer = NULL;
3517 case GST_STATE_CHANGE_READY_TO_NULL:
3518 /* remove sinks we added */
3519 if (playsink->videodeinterlacechain) {
3520 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3522 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3524 if (playsink->videochain) {
3525 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3526 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3528 if (playsink->audiochain) {
3529 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3530 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3532 if (playsink->vischain) {
3533 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3534 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3536 if (playsink->textchain) {
3537 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3538 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3540 do_async_done (playsink);
3541 /* when going to READY, keep elements around as long as possible,
3542 * so they may be re-used faster next time/url around.
3543 * when really going to NULL, clean up everything completely. */
3544 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3546 /* Unparent the sinks to allow reuse */
3547 if (playsink->videochain && playsink->videochain->sink)
3548 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3549 playsink->videochain->sink);
3550 if (playsink->audiochain && playsink->audiochain->sink)
3551 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3552 playsink->audiochain->sink);
3553 if (playsink->textchain && playsink->textchain->sink)
3554 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3555 playsink->textchain->sink);
3557 if (playsink->audio_sink != NULL)
3558 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3559 if (playsink->video_sink != NULL)
3560 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3561 if (playsink->visualisation != NULL)
3562 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3563 if (playsink->text_sink != NULL)
3564 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3566 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3567 playsink->videodeinterlacechain = NULL;
3568 free_chain ((GstPlayChain *) playsink->videochain);
3569 playsink->videochain = NULL;
3570 free_chain ((GstPlayChain *) playsink->audiochain);
3571 playsink->audiochain = NULL;
3572 free_chain ((GstPlayChain *) playsink->vischain);
3573 playsink->vischain = NULL;
3574 free_chain ((GstPlayChain *) playsink->textchain);
3575 playsink->textchain = NULL;
3586 GST_DEBUG_OBJECT (element,
3587 "element failed to change states -- activation problem?");
3588 return GST_STATE_CHANGE_FAILURE;
3593 gst_play_sink_set_property (GObject * object, guint prop_id,
3594 const GValue * value, GParamSpec * spec)
3596 GstPlaySink *playsink = GST_PLAY_SINK (object);
3600 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3603 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3606 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3608 case PROP_FONT_DESC:
3609 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3611 case PROP_SUBTITLE_ENCODING:
3612 gst_play_sink_set_subtitle_encoding (playsink,
3613 g_value_get_string (value));
3615 case PROP_VIS_PLUGIN:
3616 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3618 case PROP_AV_OFFSET:
3619 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3622 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3628 gst_play_sink_get_property (GObject * object, guint prop_id,
3629 GValue * value, GParamSpec * spec)
3631 GstPlaySink *playsink = GST_PLAY_SINK (object);
3635 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3638 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3641 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3643 case PROP_FONT_DESC:
3644 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3646 case PROP_SUBTITLE_ENCODING:
3647 g_value_take_string (value,
3648 gst_play_sink_get_subtitle_encoding (playsink));
3650 case PROP_VIS_PLUGIN:
3651 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3654 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3656 case PROP_AV_OFFSET:
3657 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3660 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3667 gst_play_sink_plugin_init (GstPlugin * plugin)
3669 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3671 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3672 GST_TYPE_PLAY_SINK);