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;
169 gulong audio_block_id;
171 GstElement *audio_tee;
172 GstPad *audio_tee_sink;
173 GstPad *audio_tee_asrc;
174 GstPad *audio_tee_vissrc;
177 gboolean video_pad_raw;
178 gboolean video_pad_blocked;
179 GstPad *video_srcpad_stream_synchronizer;
180 GstPad *video_sinkpad_stream_synchronizer;
181 gulong video_block_id;
184 gboolean text_pad_blocked;
185 GstPad *text_srcpad_stream_synchronizer;
186 GstPad *text_sinkpad_stream_synchronizer;
187 gulong text_block_id;
189 guint32 pending_blocked_pads;
192 GstElement *audio_sink;
193 GstElement *video_sink;
194 GstElement *visualisation;
195 GstElement *text_sink;
198 gchar *font_desc; /* font description */
199 gchar *subtitle_encoding; /* subtitle encoding */
200 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
202 gboolean volume_changed; /* volume/mute changed while no audiochain */
203 gboolean mute_changed; /* ... has been created yet */
207 struct _GstPlaySinkClass
209 GstBinClass parent_class;
211 gboolean (*reconfigure) (GstPlaySink * playsink);
213 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
217 static GstStaticPadTemplate audiotemplate =
218 GST_STATIC_PAD_TEMPLATE ("audio_sink",
221 GST_STATIC_CAPS_ANY);
222 static GstStaticPadTemplate videotemplate =
223 GST_STATIC_PAD_TEMPLATE ("video_sink",
226 GST_STATIC_CAPS_ANY);
227 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
230 GST_STATIC_CAPS_ANY);
232 /* FIXME 0.11: Remove */
233 static GstStaticPadTemplate audiorawtemplate =
234 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
237 GST_STATIC_CAPS_ANY);
238 static GstStaticPadTemplate videorawtemplate =
239 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
242 GST_STATIC_CAPS_ANY);
253 PROP_SUBTITLE_ENCODING,
267 static void gst_play_sink_dispose (GObject * object);
268 static void gst_play_sink_finalize (GObject * object);
269 static void gst_play_sink_set_property (GObject * object, guint prop_id,
270 const GValue * value, GParamSpec * spec);
271 static void gst_play_sink_get_property (GObject * object, guint prop_id,
272 GValue * value, GParamSpec * spec);
274 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
275 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
276 static void gst_play_sink_release_request_pad (GstElement * element,
278 static gboolean gst_play_sink_send_event (GstElement * element,
280 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
281 GstStateChange transition);
283 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
285 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
286 GstPlaySink * playsink);
287 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
288 GstPlaySink * playsink);
290 static void update_av_offset (GstPlaySink * playsink);
293 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
294 GValue * return_value G_GNUC_UNUSED,
295 guint n_param_values,
296 const GValue * param_values,
297 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
299 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
300 gpointer arg_1, gpointer data2);
301 register GMarshalFunc_OBJECT__BOXED callback;
302 register GCClosure *cc = (GCClosure *) closure;
303 register gpointer data1, data2;
305 g_return_if_fail (return_value != NULL);
306 g_return_if_fail (n_param_values == 2);
308 if (G_CCLOSURE_SWAP_DATA (closure)) {
309 data1 = closure->data;
310 data2 = g_value_peek_pointer (param_values + 0);
312 data1 = g_value_peek_pointer (param_values + 0);
313 data2 = closure->data;
316 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
318 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
320 gst_value_take_buffer (return_value, v_return);
323 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
325 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
328 gst_play_sink_class_init (GstPlaySinkClass * klass)
330 GObjectClass *gobject_klass;
331 GstElementClass *gstelement_klass;
332 GstBinClass *gstbin_klass;
334 gobject_klass = (GObjectClass *) klass;
335 gstelement_klass = (GstElementClass *) klass;
336 gstbin_klass = (GstBinClass *) klass;
338 gobject_klass->dispose = gst_play_sink_dispose;
339 gobject_klass->finalize = gst_play_sink_finalize;
340 gobject_klass->set_property = gst_play_sink_set_property;
341 gobject_klass->get_property = gst_play_sink_get_property;
347 * Control the behaviour of playsink.
349 g_object_class_install_property (gobject_klass, PROP_FLAGS,
350 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
351 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
352 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
355 * GstPlaySink:volume:
357 * Get or set the current audio stream volume. 1.0 means 100%,
358 * 0.0 means mute. This uses a linear volume scale.
361 g_object_class_install_property (gobject_klass, PROP_VOLUME,
362 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
363 0.0, VOLUME_MAX_DOUBLE, 1.0,
364 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
365 g_object_class_install_property (gobject_klass, PROP_MUTE,
366 g_param_spec_boolean ("mute", "Mute",
367 "Mute the audio channel without changing the volume", FALSE,
368 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
369 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
370 g_param_spec_string ("subtitle-font-desc",
371 "Subtitle font description",
372 "Pango font description of font "
373 "to be used for subtitle rendering", NULL,
374 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
375 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
376 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
377 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
378 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
379 "be checked for an encoding to use. If that is not set either, "
380 "ISO-8859-15 will be assumed.", NULL,
381 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
382 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
383 g_param_spec_object ("vis-plugin", "Vis plugin",
384 "the visualization element to use (NULL = default)",
385 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
389 * Get the currently rendered or prerolled frame in the video sink.
390 * The #GstCaps on the buffer will describe the format of the buffer.
394 g_object_class_install_property (gobject_klass, PROP_FRAME,
395 g_param_spec_boxed ("frame", "Frame",
396 "The last frame (NULL = no video available)",
397 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
399 * GstPlaySink:av-offset:
401 * Control the synchronisation offset between the audio and video streams.
402 * Positive values make the audio ahead of the video and negative values make
403 * the audio go behind the video.
407 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
408 g_param_spec_int64 ("av-offset", "AV Offset",
409 "The synchronisation offset between audio and video in nanoseconds",
410 G_MININT64, G_MAXINT64, 0,
411 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
414 * GstPlaySink:video-sink:
416 * Set the used video sink element. NULL will use the default sink. playsink
417 * must be in %GST_STATE_NULL
421 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
422 g_param_spec_object ("video-sink", "Video Sink",
423 "the video output element to use (NULL = default sink)",
424 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
426 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
427 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
428 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
431 * GstPlaySink::convert-frame
432 * @playsink: a #GstPlaySink
433 * @caps: the target format of the frame
435 * Action signal to retrieve the currently playing video frame in the format
436 * specified by @caps.
437 * If @caps is %NULL, no conversion will be performed and this function is
438 * equivalent to the #GstPlaySink::frame property.
440 * Returns: a #GstBuffer of the current video frame converted to #caps.
441 * The caps on the buffer will describe the final layout of the buffer data.
442 * %NULL is returned when no current buffer can be retrieved or when the
447 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
448 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
449 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
450 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
452 gst_element_class_add_pad_template (gstelement_klass,
453 gst_static_pad_template_get (&audiorawtemplate));
454 gst_element_class_add_pad_template (gstelement_klass,
455 gst_static_pad_template_get (&audiotemplate));
456 gst_element_class_add_pad_template (gstelement_klass,
457 gst_static_pad_template_get (&videorawtemplate));
458 gst_element_class_add_pad_template (gstelement_klass,
459 gst_static_pad_template_get (&videotemplate));
460 gst_element_class_add_pad_template (gstelement_klass,
461 gst_static_pad_template_get (&texttemplate));
462 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
464 "Convenience sink for multiple streams",
465 "Wim Taymans <wim.taymans@gmail.com>");
467 gstelement_klass->change_state =
468 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
469 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
470 gstelement_klass->request_new_pad =
471 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
472 gstelement_klass->release_pad =
473 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
475 gstbin_klass->handle_message =
476 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
478 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
479 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
483 gst_play_sink_init (GstPlaySink * playsink)
486 playsink->video_sink = NULL;
487 playsink->audio_sink = NULL;
488 playsink->visualisation = NULL;
489 playsink->text_sink = NULL;
490 playsink->volume = 1.0;
491 playsink->font_desc = NULL;
492 playsink->subtitle_encoding = NULL;
493 playsink->flags = DEFAULT_FLAGS;
495 playsink->stream_synchronizer =
496 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
497 gst_bin_add (GST_BIN_CAST (playsink),
498 GST_ELEMENT_CAST (playsink->stream_synchronizer));
500 g_static_rec_mutex_init (&playsink->lock);
501 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
505 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
509 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
512 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
518 free_chain (GstPlayChain * chain)
522 gst_object_unref (chain->bin);
528 gst_play_sink_dispose (GObject * object)
530 GstPlaySink *playsink;
532 playsink = GST_PLAY_SINK (object);
534 if (playsink->audio_sink != NULL) {
535 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
536 gst_object_unref (playsink->audio_sink);
537 playsink->audio_sink = NULL;
539 if (playsink->video_sink != NULL) {
540 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
541 gst_object_unref (playsink->video_sink);
542 playsink->video_sink = NULL;
544 if (playsink->visualisation != NULL) {
545 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
546 gst_object_unref (playsink->visualisation);
547 playsink->visualisation = NULL;
549 if (playsink->text_sink != NULL) {
550 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
551 gst_object_unref (playsink->text_sink);
552 playsink->text_sink = NULL;
555 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
556 playsink->videodeinterlacechain = NULL;
557 free_chain ((GstPlayChain *) playsink->videochain);
558 playsink->videochain = NULL;
559 free_chain ((GstPlayChain *) playsink->audiochain);
560 playsink->audiochain = NULL;
561 free_chain ((GstPlayChain *) playsink->vischain);
562 playsink->vischain = NULL;
563 free_chain ((GstPlayChain *) playsink->textchain);
564 playsink->textchain = NULL;
566 if (playsink->audio_tee_sink) {
567 gst_object_unref (playsink->audio_tee_sink);
568 playsink->audio_tee_sink = NULL;
571 if (playsink->audio_tee_vissrc) {
572 gst_element_release_request_pad (playsink->audio_tee,
573 playsink->audio_tee_vissrc);
574 gst_object_unref (playsink->audio_tee_vissrc);
575 playsink->audio_tee_vissrc = NULL;
578 if (playsink->audio_tee_asrc) {
579 gst_element_release_request_pad (playsink->audio_tee,
580 playsink->audio_tee_asrc);
581 gst_object_unref (playsink->audio_tee_asrc);
582 playsink->audio_tee_asrc = NULL;
585 g_free (playsink->font_desc);
586 playsink->font_desc = NULL;
588 g_free (playsink->subtitle_encoding);
589 playsink->subtitle_encoding = NULL;
591 playsink->stream_synchronizer = NULL;
593 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
597 gst_play_sink_finalize (GObject * object)
599 GstPlaySink *playsink;
601 playsink = GST_PLAY_SINK (object);
603 g_static_rec_mutex_free (&playsink->lock);
605 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
609 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
612 GstElement **elem = NULL, *old = NULL;
614 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
616 GST_PLAY_SINK_LOCK (playsink);
618 case GST_PLAY_SINK_TYPE_AUDIO:
619 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
620 elem = &playsink->audio_sink;
622 case GST_PLAY_SINK_TYPE_VIDEO:
623 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
624 elem = &playsink->video_sink;
626 case GST_PLAY_SINK_TYPE_TEXT:
627 elem = &playsink->text_sink;
635 gst_object_ref (sink);
638 GST_PLAY_SINK_UNLOCK (playsink);
642 gst_element_set_state (old, GST_STATE_NULL);
643 gst_object_unref (old);
648 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
650 GstElement *result = NULL;
651 GstElement *elem = NULL, *chainp = NULL;
653 GST_PLAY_SINK_LOCK (playsink);
655 case GST_PLAY_SINK_TYPE_AUDIO:
656 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
658 GstPlayAudioChain *chain;
659 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
660 chainp = chain->sink;
661 elem = playsink->audio_sink;
664 case GST_PLAY_SINK_TYPE_VIDEO:
665 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
667 GstPlayVideoChain *chain;
668 if ((chain = (GstPlayVideoChain *) playsink->videochain))
669 chainp = chain->sink;
670 elem = playsink->video_sink;
673 case GST_PLAY_SINK_TYPE_TEXT:
675 GstPlayTextChain *chain;
676 if ((chain = (GstPlayTextChain *) playsink->textchain))
677 chainp = chain->sink;
678 elem = playsink->text_sink;
685 /* we have an active chain with a sink, get the sink */
686 result = gst_object_ref (chainp);
688 /* nothing found, return last configured sink */
689 if (result == NULL && elem)
690 result = gst_object_ref (elem);
691 GST_PLAY_SINK_UNLOCK (playsink);
696 static GstProbeReturn
697 gst_play_sink_vis_blocked (GstPad * tee_pad, GstProbeType type,
698 gpointer type_data, gpointer user_data)
700 GstPlaySink *playsink;
701 GstPlayVisChain *chain;
703 playsink = GST_PLAY_SINK (user_data);
705 GST_PLAY_SINK_LOCK (playsink);
706 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
707 /* now try to change the plugin in the running vis chain */
708 if (!(chain = (GstPlayVisChain *) playsink->vischain))
711 /* unlink the old plugin and unghost the pad */
712 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
713 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
715 /* set the old plugin to NULL and remove */
716 gst_element_set_state (chain->vis, GST_STATE_NULL);
717 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
719 /* add new plugin and set state to playing */
720 chain->vis = playsink->visualisation;
721 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
722 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
725 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
726 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
729 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
730 GST_PAD_LINK_CHECK_NOTHING);
731 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
735 GST_PLAY_SINK_UNLOCK (playsink);
737 /* remove the probe and unblock the pad */
738 return GST_PROBE_REMOVE;
742 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
744 GstPlayVisChain *chain;
746 /* setting NULL means creating the default vis plugin */
748 vis = gst_element_factory_make ("goom", "vis");
750 /* simply return if we don't have a vis plugin here */
754 GST_PLAY_SINK_LOCK (playsink);
755 /* first store the new vis */
756 if (playsink->visualisation)
757 gst_object_unref (playsink->visualisation);
759 gst_object_ref_sink (vis);
760 playsink->visualisation = vis;
762 /* now try to change the plugin in the running vis chain, if we have no chain,
763 * we don't bother, any future vis chain will be created with the new vis
765 if (!(chain = (GstPlayVisChain *) playsink->vischain))
768 /* block the pad, the next time the callback is called we can change the
769 * visualisation. It's possible that this never happens or that the pad was
770 * already blocked. If the callback never happens, we don't have new data so
771 * we don't need the new vis plugin. If the pad was already blocked, the
772 * function returns FALSE but the previous pad block will do the right thing
774 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
775 gst_pad_add_probe (chain->blockpad, GST_PROBE_TYPE_BLOCK,
776 gst_play_sink_vis_blocked, playsink, NULL);
778 GST_PLAY_SINK_UNLOCK (playsink);
784 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
786 GstElement *result = NULL;
787 GstPlayVisChain *chain;
789 GST_PLAY_SINK_LOCK (playsink);
790 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
791 /* we have an active chain, get the sink */
793 result = gst_object_ref (chain->vis);
795 /* nothing found, return last configured sink */
796 if (result == NULL && playsink->visualisation)
797 result = gst_object_ref (playsink->visualisation);
798 GST_PLAY_SINK_UNLOCK (playsink);
804 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
806 GstPlayAudioChain *chain;
808 GST_PLAY_SINK_LOCK (playsink);
809 playsink->volume = volume;
810 chain = (GstPlayAudioChain *) playsink->audiochain;
811 if (chain && chain->volume) {
812 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
813 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
814 chain->mute, volume, playsink->mute);
815 /* if there is a mute element or we are not muted, set the volume */
816 if (chain->mute || !playsink->mute)
817 g_object_set (chain->volume, "volume", volume, NULL);
819 GST_LOG_OBJECT (playsink, "no volume element");
820 playsink->volume_changed = TRUE;
822 GST_PLAY_SINK_UNLOCK (playsink);
826 gst_play_sink_get_volume (GstPlaySink * playsink)
829 GstPlayAudioChain *chain;
831 GST_PLAY_SINK_LOCK (playsink);
832 chain = (GstPlayAudioChain *) playsink->audiochain;
833 result = playsink->volume;
834 if (chain && chain->volume) {
835 if (chain->mute || !playsink->mute) {
836 g_object_get (chain->volume, "volume", &result, NULL);
837 playsink->volume = result;
840 GST_PLAY_SINK_UNLOCK (playsink);
846 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
848 GstPlayAudioChain *chain;
850 GST_PLAY_SINK_LOCK (playsink);
851 playsink->mute = mute;
852 chain = (GstPlayAudioChain *) playsink->audiochain;
855 g_object_set (chain->mute, "mute", mute, NULL);
856 } else if (chain->volume) {
858 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
860 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
864 playsink->mute_changed = TRUE;
866 GST_PLAY_SINK_UNLOCK (playsink);
870 gst_play_sink_get_mute (GstPlaySink * playsink)
873 GstPlayAudioChain *chain;
875 GST_PLAY_SINK_LOCK (playsink);
876 chain = (GstPlayAudioChain *) playsink->audiochain;
877 if (chain && chain->mute) {
878 g_object_get (chain->mute, "mute", &result, NULL);
879 playsink->mute = result;
881 result = playsink->mute;
883 GST_PLAY_SINK_UNLOCK (playsink);
889 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
893 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
894 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
898 add_chain (GstPlayChain * chain, gboolean add)
900 if (chain->added == add)
904 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
906 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
907 /* we don't want to lose our sink status */
908 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
917 activate_chain (GstPlayChain * chain, gboolean activate)
921 if (chain->activated == activate)
924 GST_OBJECT_LOCK (chain->playsink);
925 state = GST_STATE_TARGET (chain->playsink);
926 GST_OBJECT_UNLOCK (chain->playsink);
929 gst_element_set_state (chain->bin, state);
931 gst_element_set_state (chain->bin, GST_STATE_NULL);
933 chain->activated = activate;
939 element_is_sink (GstElement * element)
943 GST_OBJECT_LOCK (element);
944 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
945 GST_OBJECT_UNLOCK (element);
947 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
952 element_has_property (GstElement * element, const gchar * pname, GType type)
956 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
959 GST_DEBUG_OBJECT (element, "no %s property", pname);
963 if (type == G_TYPE_INVALID || type == pspec->value_type ||
964 g_type_is_a (pspec->value_type, type)) {
965 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
966 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
970 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
971 "and we expected it to be of type %s", pname,
972 g_type_name (pspec->value_type), g_type_name (type));
979 const gchar *prop_name;
982 } FindPropertyHelper;
985 find_property (const GValue * item, FindPropertyHelper * helper)
987 GstElement *element = g_value_get_object (item);
988 if (helper->need_sink && !element_is_sink (element)) {
992 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
996 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
997 (helper->need_sink) ? "sink" : "element");
998 return 0; /* keep it */
1001 /* FIXME: why not move these functions into core? */
1002 /* find a sink in the hierarchy with a property named @name. This function does
1003 * not increase the refcount of the returned object and thus remains valid as
1004 * long as the bin is valid. */
1006 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1007 const gchar * name, GType expected_type)
1009 GstElement *result = NULL;
1012 if (element_has_property (obj, name, expected_type)) {
1014 } else if (GST_IS_BIN (obj)) {
1016 GValue item = { 0, };
1017 FindPropertyHelper helper = { name, expected_type, TRUE };
1019 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1020 found = gst_iterator_find_custom (it,
1021 (GCompareFunc) find_property, &item, &helper);
1022 gst_iterator_free (it);
1024 result = g_value_get_object (&item);
1025 /* we don't need the extra ref */
1026 g_value_unset (&item);
1032 /* find an object in the hierarchy with a property named @name */
1034 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1035 const gchar * name, GType expected_type)
1037 GstElement *result = NULL;
1040 if (GST_IS_BIN (obj)) {
1042 GValue item = { 0, };
1043 FindPropertyHelper helper = { name, expected_type, FALSE };
1045 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1046 found = gst_iterator_find_custom (it,
1047 (GCompareFunc) find_property, &item, &helper);
1048 gst_iterator_free (it);
1050 result = g_value_dup_object (&item);
1051 g_value_unset (&item);
1053 if (element_has_property (obj, name, expected_type)) {
1055 gst_object_ref (obj);
1062 do_async_start (GstPlaySink * playsink)
1064 GstMessage *message;
1066 if (!playsink->need_async_start) {
1067 GST_INFO_OBJECT (playsink, "no async_start needed");
1071 playsink->async_pending = TRUE;
1073 GST_INFO_OBJECT (playsink, "Sending async_start message");
1074 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1075 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1076 (playsink), message);
1080 do_async_done (GstPlaySink * playsink)
1082 GstMessage *message;
1084 if (playsink->async_pending) {
1085 GST_INFO_OBJECT (playsink, "Sending async_done message");
1086 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1087 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1088 (playsink), message);
1090 playsink->async_pending = FALSE;
1093 playsink->need_async_start = FALSE;
1096 /* try to change the state of an element. This function returns the element when
1097 * the state change could be performed. When this function returns NULL an error
1098 * occured and the element is unreffed if @unref is TRUE. */
1100 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1102 GstStateChangeReturn ret;
1105 ret = gst_element_set_state (element, GST_STATE_READY);
1106 if (ret == GST_STATE_CHANGE_FAILURE) {
1107 GST_DEBUG_OBJECT (playsink, "failed state change..");
1108 gst_element_set_state (element, GST_STATE_NULL);
1110 gst_object_unref (element);
1117 /* make the element (bin) that contains the elements needed to perform
1118 * video display. Only used for *raw* video streams.
1120 * +------------------------------------------------------------+
1122 * | +-------+ +----------+ +----------+ +---------+ |
1123 * | | queue | |colorspace| |videoscale| |videosink| |
1124 * | +-sink src-sink src-sink src-sink | |
1125 * | | +-------+ +----------+ +----------+ +---------+ |
1127 * +------------------------------------------------------------+
1130 static GstPlayVideoDeinterlaceChain *
1131 gen_video_deinterlace_chain (GstPlaySink * playsink)
1133 GstPlayVideoDeinterlaceChain *chain;
1136 GstElement *head = NULL, *prev = NULL;
1138 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1139 chain->chain.playsink = playsink;
1141 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1143 /* create a bin to hold objects, as we create them we add them to this bin so
1144 * that when something goes wrong we only need to unref the bin */
1145 chain->chain.bin = gst_bin_new ("vdbin");
1146 bin = GST_BIN_CAST (chain->chain.bin);
1147 gst_object_ref_sink (bin);
1149 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1150 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1151 if (chain->conv == NULL) {
1152 post_missing_element_message (playsink, COLORSPACE);
1153 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1154 (_("Missing element '%s' - check your GStreamer installation."),
1155 COLORSPACE), ("video rendering might fail"));
1157 gst_bin_add (bin, chain->conv);
1162 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1163 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1164 if (chain->deinterlace == NULL) {
1165 post_missing_element_message (playsink, "deinterlace");
1166 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1167 (_("Missing element '%s' - check your GStreamer installation."),
1168 "deinterlace"), ("deinterlacing won't work"));
1170 gst_bin_add (bin, chain->deinterlace);
1172 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1173 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1176 head = chain->deinterlace;
1178 prev = chain->deinterlace;
1182 pad = gst_element_get_static_pad (head, "sink");
1183 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1184 gst_object_unref (pad);
1186 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1190 pad = gst_element_get_static_pad (prev, "src");
1191 chain->srcpad = gst_ghost_pad_new ("src", pad);
1192 gst_object_unref (pad);
1194 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1197 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1198 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1204 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1205 (NULL), ("Failed to configure the video deinterlace chain."));
1206 free_chain ((GstPlayChain *) chain);
1211 /* make the element (bin) that contains the elements needed to perform
1214 * +------------------------------------------------------------+
1216 * | +-------+ +----------+ +----------+ +---------+ |
1217 * | | queue | |colorspace| |videoscale| |videosink| |
1218 * | +-sink src-sink src-sink src-sink | |
1219 * | | +-------+ +----------+ +----------+ +---------+ |
1221 * +------------------------------------------------------------+
1224 static GstPlayVideoChain *
1225 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1227 GstPlayVideoChain *chain;
1230 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1232 chain = g_new0 (GstPlayVideoChain, 1);
1233 chain->chain.playsink = playsink;
1234 chain->chain.raw = raw;
1236 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1238 if (playsink->video_sink) {
1239 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1240 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1242 /* only try fallback if no specific sink was chosen */
1243 if (chain->sink == NULL) {
1244 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1245 elem = gst_element_factory_make ("autovideosink", "videosink");
1246 chain->sink = try_element (playsink, elem, TRUE);
1248 if (chain->sink == NULL) {
1249 /* if default sink from config.h is different then try it too */
1250 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1251 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1252 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1253 chain->sink = try_element (playsink, elem, TRUE);
1257 playsink->video_sink = gst_object_ref (chain->sink);
1259 if (chain->sink == NULL)
1263 /* if we can disable async behaviour of the sink, we can avoid adding a
1264 * queue for the audio chain. */
1266 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1269 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1270 async, GST_ELEMENT_NAME (elem));
1271 g_object_set (elem, "async", async, NULL);
1272 chain->async = async;
1274 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1275 chain->async = TRUE;
1278 /* find ts-offset element */
1279 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1280 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1283 /* create a bin to hold objects, as we create them we add them to this bin so
1284 * that when something goes wrong we only need to unref the bin */
1285 chain->chain.bin = gst_bin_new ("vbin");
1286 bin = GST_BIN_CAST (chain->chain.bin);
1287 gst_object_ref_sink (bin);
1288 gst_bin_add (bin, chain->sink);
1290 /* decouple decoder from sink, this improves playback quite a lot since the
1291 * decoder can continue while the sink blocks for synchronisation. We don't
1292 * need a lot of buffers as this consumes a lot of memory and we don't want
1293 * too little because else we would be context switching too quickly. */
1294 chain->queue = gst_element_factory_make ("queue", "vqueue");
1295 if (chain->queue == NULL) {
1296 post_missing_element_message (playsink, "queue");
1297 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1298 (_("Missing element '%s' - check your GStreamer installation."),
1299 "queue"), ("video rendering might be suboptimal"));
1303 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1304 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1305 gst_bin_add (bin, chain->queue);
1306 head = prev = chain->queue;
1309 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1310 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1312 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1313 gst_bin_add (bin, chain->conv);
1315 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1316 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1325 GST_DEBUG_OBJECT (playsink, "linking to sink");
1326 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1327 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1331 pad = gst_element_get_static_pad (head, "sink");
1332 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1333 gst_object_unref (pad);
1335 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1342 if (!elem && !playsink->video_sink) {
1343 post_missing_element_message (playsink, "autovideosink");
1344 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1345 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1346 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1347 (_("Both autovideosink and %s elements are missing."),
1348 DEFAULT_VIDEOSINK), (NULL));
1350 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1351 (_("The autovideosink element is missing.")), (NULL));
1354 if (playsink->video_sink) {
1355 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1356 (_("Configured videosink %s is not working."),
1357 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1358 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1359 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1360 (_("Both autovideosink and %s elements are not working."),
1361 DEFAULT_VIDEOSINK), (NULL));
1363 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1364 (_("The autovideosink element is not working.")), (NULL));
1367 free_chain ((GstPlayChain *) chain);
1372 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1373 (NULL), ("Failed to configure the video sink."));
1374 /* checking sink made it READY */
1375 gst_element_set_state (chain->sink, GST_STATE_NULL);
1376 /* Remove chain from the bin to allow reuse later */
1377 gst_bin_remove (bin, chain->sink);
1378 free_chain ((GstPlayChain *) chain);
1384 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1387 GstPlayVideoChain *chain;
1388 GstStateChangeReturn ret;
1390 chain = playsink->videochain;
1392 chain->chain.raw = raw;
1394 /* if the chain was active we don't do anything */
1395 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1398 /* try to set the sink element to READY again */
1399 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1400 if (ret == GST_STATE_CHANGE_FAILURE)
1403 /* find ts-offset element */
1405 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1406 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1409 /* if we can disable async behaviour of the sink, we can avoid adding a
1410 * queue for the audio chain. */
1412 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1415 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1416 async, GST_ELEMENT_NAME (elem));
1417 g_object_set (elem, "async", async, NULL);
1418 chain->async = async;
1420 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1421 chain->async = TRUE;
1426 /* make an element for playback of video with subtitles embedded.
1427 * Only used for *raw* video streams.
1429 * +--------------------------------------------+
1431 * | +--------+ +-----------------+ |
1432 * | | queue | | subtitleoverlay | |
1433 * video--src sink---video_sink | |
1434 * | +--------+ | src--src
1435 * text------------------text_sink | |
1436 * | +-----------------+ |
1437 * +--------------------------------------------+
1440 static GstPlayTextChain *
1441 gen_text_chain (GstPlaySink * playsink)
1443 GstPlayTextChain *chain;
1446 GstPad *videosinkpad, *textsinkpad, *srcpad;
1448 chain = g_new0 (GstPlayTextChain, 1);
1449 chain->chain.playsink = playsink;
1451 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1453 chain->chain.bin = gst_bin_new ("tbin");
1454 bin = GST_BIN_CAST (chain->chain.bin);
1455 gst_object_ref_sink (bin);
1457 videosinkpad = textsinkpad = srcpad = NULL;
1459 /* first try to hook the text pad to the custom sink */
1460 if (playsink->text_sink) {
1461 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1462 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1465 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1468 /* make sure the sparse subtitles don't participate in the preroll */
1469 g_object_set (elem, "async", FALSE, NULL);
1470 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1471 gst_bin_add (bin, chain->sink);
1472 /* NOTE streamsynchronizer needs streams decoupled */
1473 /* make a little queue */
1474 chain->queue = gst_element_factory_make ("queue", "subqueue");
1475 if (chain->queue == NULL) {
1476 post_missing_element_message (playsink, "queue");
1477 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1478 (_("Missing element '%s' - check your GStreamer installation."),
1479 "queue"), ("rendering might be suboptimal"));
1481 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1482 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1483 "silent", TRUE, NULL);
1484 gst_bin_add (bin, chain->queue);
1486 /* we have a custom sink, this will be our textsinkpad */
1487 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1488 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1489 /* we're all fine now and we can add the sink to the chain */
1490 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1491 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1493 GST_WARNING_OBJECT (playsink,
1494 "can't find a sink pad on custom text sink");
1495 gst_bin_remove (bin, chain->sink);
1496 gst_bin_remove (bin, chain->queue);
1498 chain->queue = NULL;
1500 /* try to set sync to true but it's no biggie when we can't */
1502 gst_play_sink_find_property_sinks (playsink, chain->sink,
1503 "sync", G_TYPE_BOOLEAN)))
1504 g_object_set (elem, "sync", TRUE, NULL);
1507 gst_bin_remove (bin, chain->sink);
1509 GST_WARNING_OBJECT (playsink,
1510 "can't find async property in custom text sink");
1513 if (textsinkpad == NULL) {
1514 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1515 (_("Custom text sink element is not usable.")),
1516 ("fallback to default textoverlay"));
1520 if (textsinkpad == NULL) {
1521 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1522 /* make a little queue */
1523 chain->queue = gst_element_factory_make ("queue", "vqueue");
1524 if (chain->queue == NULL) {
1525 post_missing_element_message (playsink, "queue");
1526 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1527 (_("Missing element '%s' - check your GStreamer installation."),
1528 "queue"), ("video rendering might be suboptimal"));
1530 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1531 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1532 "silent", TRUE, NULL);
1533 gst_bin_add (bin, chain->queue);
1534 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1538 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1539 if (chain->overlay == NULL) {
1540 post_missing_element_message (playsink, "subtitleoverlay");
1541 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1542 (_("Missing element '%s' - check your GStreamer installation."),
1543 "subtitleoverlay"), ("subtitle rendering disabled"));
1545 GstElement *element;
1547 gst_bin_add (bin, chain->overlay);
1549 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1550 if (playsink->font_desc) {
1551 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1552 playsink->font_desc, NULL);
1554 if (playsink->subtitle_encoding) {
1555 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1556 playsink->subtitle_encoding, NULL);
1559 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1560 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1562 /* make another little queue to decouple streams */
1563 element = gst_element_factory_make ("queue", "subqueue");
1564 if (element == NULL) {
1565 post_missing_element_message (playsink, "queue");
1566 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1567 (_("Missing element '%s' - check your GStreamer installation."),
1568 "queue"), ("rendering might be suboptimal"));
1570 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1571 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1572 "silent", TRUE, NULL);
1573 gst_bin_add (bin, element);
1574 gst_element_link_pads_full (element, "src", chain->overlay,
1575 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1576 textsinkpad = gst_element_get_static_pad (element, "sink");
1577 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1583 if (videosinkpad == NULL) {
1584 /* if we still don't have a videosink, we don't have an overlay. the only
1585 * thing we can do is insert an identity and ghost the src
1587 chain->identity = gst_element_factory_make ("identity", "tidentity");
1588 if (chain->identity == NULL) {
1589 post_missing_element_message (playsink, "identity");
1590 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1591 (_("Missing element '%s' - check your GStreamer installation."),
1592 "identity"), (NULL));
1594 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1595 g_object_set (chain->identity, "silent", TRUE, NULL);
1596 gst_bin_add (bin, chain->identity);
1597 srcpad = gst_element_get_static_pad (chain->identity, "src");
1598 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1602 /* expose the ghostpads */
1604 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1605 gst_object_unref (videosinkpad);
1606 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1609 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1610 gst_object_unref (textsinkpad);
1611 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1614 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1615 gst_object_unref (srcpad);
1616 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1623 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1627 g_object_get (object, "volume", &vol, NULL);
1628 playsink->volume = vol;
1630 g_object_notify (G_OBJECT (playsink), "volume");
1634 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1638 g_object_get (object, "mute", &mute, NULL);
1639 playsink->mute = mute;
1641 g_object_notify (G_OBJECT (playsink), "mute");
1644 /* make the chain that contains the elements needed to perform
1647 * We add a tee as the first element so that we can link the visualisation chain
1648 * to it when requested.
1650 * +-------------------------------------------------------------+
1652 * | +---------+ +----------+ +---------+ +---------+ |
1653 * | |audioconv| |audioscale| | volume | |audiosink| |
1654 * | +-srck src-sink src-sink src-sink | |
1655 * | | +---------+ +----------+ +---------+ +---------+ |
1657 * +-------------------------------------------------------------+
1659 static GstPlayAudioChain *
1660 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1662 GstPlayAudioChain *chain;
1664 gboolean have_volume;
1666 GstElement *head, *prev, *elem = NULL;
1668 chain = g_new0 (GstPlayAudioChain, 1);
1669 chain->chain.playsink = playsink;
1670 chain->chain.raw = raw;
1672 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1674 if (playsink->audio_sink) {
1675 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1676 playsink->audio_sink);
1677 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1679 /* only try fallback if no specific sink was chosen */
1680 if (chain->sink == NULL) {
1681 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1682 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1683 chain->sink = try_element (playsink, elem, TRUE);
1685 if (chain->sink == NULL) {
1686 /* if default sink from config.h is different then try it too */
1687 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1688 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1689 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1690 chain->sink = try_element (playsink, elem, TRUE);
1694 playsink->audio_sink = gst_object_ref (chain->sink);
1696 if (chain->sink == NULL)
1699 chain->chain.bin = gst_bin_new ("abin");
1700 bin = GST_BIN_CAST (chain->chain.bin);
1701 gst_object_ref_sink (bin);
1702 gst_bin_add (bin, chain->sink);
1704 /* we have to add a queue when we need to decouple for the video sink in
1705 * visualisations and for streamsynchronizer */
1706 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1707 chain->queue = gst_element_factory_make ("queue", "aqueue");
1708 if (chain->queue == NULL) {
1709 post_missing_element_message (playsink, "queue");
1710 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1711 (_("Missing element '%s' - check your GStreamer installation."),
1712 "queue"), ("audio playback and visualizations might not work"));
1716 g_object_set (chain->queue, "silent", TRUE, NULL);
1717 gst_bin_add (bin, chain->queue);
1718 prev = head = chain->queue;
1721 /* find ts-offset element */
1722 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1723 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1726 /* check if the sink, or something within the sink, has the volume property.
1727 * If it does we don't need to add a volume element. */
1729 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1732 chain->volume = elem;
1734 g_signal_connect (chain->volume, "notify::volume",
1735 G_CALLBACK (notify_volume_cb), playsink);
1737 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1739 chain->sink_volume = TRUE;
1740 /* if the sink also has a mute property we can use this as well. We'll only
1741 * use the mute property if there is a volume property. We can simulate the
1742 * mute with the volume otherwise. */
1744 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1747 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1748 g_signal_connect (chain->mute, "notify::mute",
1749 G_CALLBACK (notify_mute_cb), playsink);
1751 /* use the sink to control the volume and mute */
1752 if (playsink->volume_changed) {
1753 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1754 playsink->volume_changed = FALSE;
1756 if (playsink->mute_changed) {
1758 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1761 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1763 playsink->mute_changed = FALSE;
1766 /* no volume, we need to add a volume element when we can */
1767 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1768 have_volume = FALSE;
1769 chain->sink_volume = FALSE;
1772 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1773 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1774 GST_DEBUG_OBJECT (playsink, "creating audioconvert");
1776 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL);
1777 gst_bin_add (bin, chain->conv);
1779 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1780 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1787 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_converters =
1788 !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1789 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = (!have_volume
1790 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
1792 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1793 GstPlaySinkAudioConvert *conv =
1794 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1797 chain->volume = conv->volume;
1800 g_signal_connect (chain->volume, "notify::volume",
1801 G_CALLBACK (notify_volume_cb), playsink);
1803 /* volume also has the mute property */
1804 chain->mute = chain->volume;
1805 g_signal_connect (chain->mute, "notify::mute",
1806 G_CALLBACK (notify_mute_cb), playsink);
1808 /* configure with the latest volume and mute */
1809 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1811 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1817 /* we only have to link to the previous element if we have something in
1818 * front of the sink */
1819 GST_DEBUG_OBJECT (playsink, "linking to sink");
1820 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1821 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1825 /* post a warning if we have no way to configure the volume */
1827 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1828 (_("No volume control found")), ("Volume/mute is not available"));
1831 /* and ghost the sinkpad of the headmost element */
1832 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1833 pad = gst_element_get_static_pad (head, "sink");
1834 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1835 gst_object_unref (pad);
1836 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1843 if (!elem && !playsink->audio_sink) {
1844 post_missing_element_message (playsink, "autoaudiosink");
1845 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1846 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1847 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1848 (_("Both autoaudiosink and %s elements are missing."),
1849 DEFAULT_AUDIOSINK), (NULL));
1851 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1852 (_("The autoaudiosink element is missing.")), (NULL));
1855 if (playsink->audio_sink) {
1856 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1857 (_("Configured audiosink %s is not working."),
1858 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1859 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1860 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1861 (_("Both autoaudiosink and %s elements are not working."),
1862 DEFAULT_AUDIOSINK), (NULL));
1864 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1865 (_("The autoaudiosink element is not working.")), (NULL));
1868 free_chain ((GstPlayChain *) chain);
1873 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1874 (NULL), ("Failed to configure the audio sink."));
1875 /* checking sink made it READY */
1876 gst_element_set_state (chain->sink, GST_STATE_NULL);
1877 /* Remove chain from the bin to allow reuse later */
1878 gst_bin_remove (bin, chain->sink);
1879 free_chain ((GstPlayChain *) chain);
1885 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1888 GstPlayAudioChain *chain;
1889 GstStateChangeReturn ret;
1891 chain = playsink->audiochain;
1893 chain->chain.raw = raw;
1895 /* if the chain was active we don't do anything */
1896 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1899 /* try to set the sink element to READY again */
1900 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1901 if (ret == GST_STATE_CHANGE_FAILURE)
1904 /* find ts-offset element */
1905 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1906 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1909 /* check if the sink, or something within the sink, has the volume property.
1910 * If it does we don't need to add a volume element. */
1912 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1915 chain->volume = elem;
1917 if (playsink->volume_changed) {
1918 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1920 /* use the sink to control the volume */
1921 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1922 playsink->volume_changed = FALSE;
1925 g_signal_connect (chain->volume, "notify::volume",
1926 G_CALLBACK (notify_volume_cb), playsink);
1927 /* if the sink also has a mute property we can use this as well. We'll only
1928 * use the mute property if there is a volume property. We can simulate the
1929 * mute with the volume otherwise. */
1931 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1934 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1935 g_signal_connect (chain->mute, "notify::mute",
1936 G_CALLBACK (notify_mute_cb), playsink);
1939 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE;
1941 GstPlaySinkAudioConvert *conv =
1942 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1944 /* no volume, we need to add a volume element when we can */
1945 conv->use_volume = TRUE;
1946 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1948 /* Disconnect signals */
1949 disconnect_chain (chain, playsink);
1952 chain->volume = conv->volume;
1953 chain->mute = chain->volume;
1955 g_signal_connect (chain->volume, "notify::volume",
1956 G_CALLBACK (notify_volume_cb), playsink);
1958 g_signal_connect (chain->mute, "notify::mute",
1959 G_CALLBACK (notify_mute_cb), playsink);
1961 /* configure with the latest volume and mute */
1962 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1963 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1966 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
1972 * +-------------------------------------------------------------------+
1974 * | +----------+ +------------+ +----------+ +-------+ |
1975 * | | visqueue | | audioconv | | audiores | | vis | |
1976 * | +-sink src-sink + samp src-sink src-sink src-+ |
1977 * | | +----------+ +------------+ +----------+ +-------+ | |
1979 * +-------------------------------------------------------------------+
1982 static GstPlayVisChain *
1983 gen_vis_chain (GstPlaySink * playsink)
1985 GstPlayVisChain *chain;
1991 chain = g_new0 (GstPlayVisChain, 1);
1992 chain->chain.playsink = playsink;
1994 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
1996 chain->chain.bin = gst_bin_new ("visbin");
1997 bin = GST_BIN_CAST (chain->chain.bin);
1998 gst_object_ref_sink (bin);
2000 /* we're queuing raw audio here, we can remove this queue when we can disable
2001 * async behaviour in the video sink. */
2002 chain->queue = gst_element_factory_make ("queue", "visqueue");
2003 if (chain->queue == NULL)
2005 g_object_set (chain->queue, "silent", TRUE, NULL);
2006 gst_bin_add (bin, chain->queue);
2008 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2009 if (chain->conv == NULL)
2010 goto no_audioconvert;
2011 gst_bin_add (bin, chain->conv);
2013 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2014 if (chain->resample == NULL)
2015 goto no_audioresample;
2016 gst_bin_add (bin, chain->resample);
2018 /* this pad will be used for blocking the dataflow and switching the vis
2020 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2022 if (playsink->visualisation) {
2023 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2024 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2026 if (chain->vis == NULL) {
2027 GST_DEBUG_OBJECT (playsink, "trying goom");
2028 elem = gst_element_factory_make ("goom", "vis");
2029 chain->vis = try_element (playsink, elem, TRUE);
2031 if (chain->vis == NULL)
2034 gst_bin_add (bin, chain->vis);
2036 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2037 GST_PAD_LINK_CHECK_NOTHING);
2039 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2040 GST_PAD_LINK_CHECK_NOTHING);
2042 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2043 GST_PAD_LINK_CHECK_NOTHING);
2047 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2048 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2050 pad = gst_element_get_static_pad (chain->queue, "sink");
2051 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2052 gst_object_unref (pad);
2053 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2055 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2056 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2063 post_missing_element_message (playsink, "queue");
2064 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2065 (_("Missing element '%s' - check your GStreamer installation."),
2067 free_chain ((GstPlayChain *) chain);
2072 post_missing_element_message (playsink, "audioconvert");
2073 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2074 (_("Missing element '%s' - check your GStreamer installation."),
2075 "audioconvert"), ("possibly a liboil version mismatch?"));
2076 free_chain ((GstPlayChain *) chain);
2081 post_missing_element_message (playsink, "audioresample");
2082 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2083 (_("Missing element '%s' - check your GStreamer installation."),
2084 "audioresample"), (NULL));
2085 free_chain ((GstPlayChain *) chain);
2090 post_missing_element_message (playsink, "goom");
2091 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2092 (_("Missing element '%s' - check your GStreamer installation."),
2094 free_chain ((GstPlayChain *) chain);
2099 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2100 (NULL), ("Failed to configure the visualisation element."));
2101 /* element made it to READY */
2102 gst_element_set_state (chain->vis, GST_STATE_NULL);
2103 free_chain ((GstPlayChain *) chain);
2108 /* this function is called when all the request pads are requested and when we
2109 * have to construct the final pipeline. Based on the flags we construct the
2110 * final output pipelines.
2113 gst_play_sink_reconfigure (GstPlaySink * playsink)
2116 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2118 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2120 /* assume we need nothing */
2121 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2123 GST_PLAY_SINK_LOCK (playsink);
2124 GST_OBJECT_LOCK (playsink);
2125 /* get flags, there are protected with the object lock */
2126 flags = playsink->flags;
2127 GST_OBJECT_UNLOCK (playsink);
2129 /* figure out which components we need */
2130 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2131 /* we have subtitles and we are requested to show it */
2135 if (((flags & GST_PLAY_FLAG_VIDEO)
2136 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2137 /* we have video and we are requested to show it */
2140 /* we only deinterlace if native video is not requested and
2141 * we have raw video */
2142 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2143 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2144 need_deinterlace = TRUE;
2147 if (playsink->audio_pad) {
2148 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2151 if (playsink->audio_pad_raw) {
2152 /* only can do vis with raw uncompressed audio */
2153 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2154 /* also add video when we add visualisation */
2161 /* we have a text_pad and we need text rendering, in this case we need a
2162 * video_pad to combine the video with the text or visualizations */
2163 if (need_text && !need_video) {
2164 if (playsink->video_pad) {
2166 } else if (need_audio) {
2167 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2168 (_("Can't play a text file without video or visualizations.")),
2169 ("Have text pad but no video pad or visualizations"));
2172 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2173 (_("Can't play a text file without video or visualizations.")),
2174 ("Have text pad but no video pad or visualizations"));
2175 GST_PLAY_SINK_UNLOCK (playsink);
2180 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2181 need_video, need_vis, need_text);
2183 /* set up video pipeline */
2185 gboolean raw, async;
2187 /* we need a raw sink when we do vis or when we have a raw pad */
2188 raw = need_vis ? TRUE : playsink->video_pad_raw;
2189 /* we try to set the sink async=FALSE when we need vis, this way we can
2190 * avoid a queue in the audio chain. */
2193 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2194 playsink->video_pad_raw);
2196 if (playsink->videochain) {
2197 /* try to reactivate the chain */
2198 if (!setup_video_chain (playsink, raw, async)) {
2199 if (playsink->video_sinkpad_stream_synchronizer) {
2200 gst_element_release_request_pad (GST_ELEMENT_CAST
2201 (playsink->stream_synchronizer),
2202 playsink->video_sinkpad_stream_synchronizer);
2203 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2204 playsink->video_sinkpad_stream_synchronizer = NULL;
2205 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2206 playsink->video_srcpad_stream_synchronizer = NULL;
2209 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2211 /* Remove the sink from the bin to keep its state
2212 * and unparent it to allow reuse */
2213 if (playsink->videochain->sink)
2214 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2215 playsink->videochain->sink);
2217 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2218 free_chain ((GstPlayChain *) playsink->videochain);
2219 playsink->videochain = NULL;
2223 if (!playsink->videochain)
2224 playsink->videochain = gen_video_chain (playsink, raw, async);
2225 if (!playsink->videochain)
2228 if (!playsink->video_sinkpad_stream_synchronizer) {
2229 GValue item = { 0, };
2232 playsink->video_sinkpad_stream_synchronizer =
2233 gst_element_get_request_pad (GST_ELEMENT_CAST
2234 (playsink->stream_synchronizer), "sink_%d");
2235 it = gst_pad_iterate_internal_links
2236 (playsink->video_sinkpad_stream_synchronizer);
2238 gst_iterator_next (it, &item);
2239 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2240 g_value_unset (&item);
2241 g_assert (playsink->video_srcpad_stream_synchronizer);
2242 gst_iterator_free (it);
2245 if (playsink->video_pad)
2246 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2247 playsink->video_sinkpad_stream_synchronizer);
2249 if (need_deinterlace) {
2250 if (!playsink->videodeinterlacechain)
2251 playsink->videodeinterlacechain =
2252 gen_video_deinterlace_chain (playsink);
2253 if (!playsink->videodeinterlacechain)
2256 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2258 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2260 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2261 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2263 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2264 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2266 if (playsink->videodeinterlacechain) {
2267 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2268 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2273 GST_DEBUG_OBJECT (playsink, "adding video chain");
2274 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2275 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2276 /* if we are not part of vis or subtitles, set the ghostpad target */
2277 if (!need_vis && !need_text && (!playsink->textchain
2278 || !playsink->text_pad)) {
2279 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2280 if (need_deinterlace)
2281 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2282 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2284 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2285 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2288 GST_DEBUG_OBJECT (playsink, "no video needed");
2289 if (playsink->videochain) {
2290 GST_DEBUG_OBJECT (playsink, "removing video chain");
2291 if (playsink->vischain) {
2294 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2296 /* also had visualisation, release the tee srcpad before we then
2297 * unlink the video from it */
2298 if (playsink->audio_tee_vissrc) {
2299 gst_element_release_request_pad (playsink->audio_tee,
2300 playsink->audio_tee_vissrc);
2301 gst_object_unref (playsink->audio_tee_vissrc);
2302 playsink->audio_tee_vissrc = NULL;
2305 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2306 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2309 if (playsink->video_sinkpad_stream_synchronizer) {
2310 gst_element_release_request_pad (GST_ELEMENT_CAST
2311 (playsink->stream_synchronizer),
2312 playsink->video_sinkpad_stream_synchronizer);
2313 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2314 playsink->video_sinkpad_stream_synchronizer = NULL;
2315 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2316 playsink->video_srcpad_stream_synchronizer = NULL;
2319 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2320 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2321 if (playsink->videochain->ts_offset)
2322 gst_object_unref (playsink->videochain->ts_offset);
2323 playsink->videochain->ts_offset = NULL;
2326 if (playsink->videodeinterlacechain) {
2327 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2328 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2331 if (playsink->video_pad)
2332 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2338 GST_DEBUG_OBJECT (playsink, "adding audio");
2340 /* get a raw sink if we are asked for a raw pad */
2341 raw = playsink->audio_pad_raw;
2343 if (playsink->audiochain) {
2344 /* try to reactivate the chain */
2345 if (!setup_audio_chain (playsink, raw)) {
2346 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2347 if (playsink->audio_tee_asrc) {
2348 gst_element_release_request_pad (playsink->audio_tee,
2349 playsink->audio_tee_asrc);
2350 gst_object_unref (playsink->audio_tee_asrc);
2351 playsink->audio_tee_asrc = NULL;
2354 if (playsink->audio_sinkpad_stream_synchronizer) {
2355 gst_element_release_request_pad (GST_ELEMENT_CAST
2356 (playsink->stream_synchronizer),
2357 playsink->audio_sinkpad_stream_synchronizer);
2358 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2359 playsink->audio_sinkpad_stream_synchronizer = NULL;
2360 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2361 playsink->audio_srcpad_stream_synchronizer = NULL;
2364 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2366 /* Remove the sink from the bin to keep its state
2367 * and unparent it to allow reuse */
2368 if (playsink->audiochain->sink)
2369 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2370 playsink->audiochain->sink);
2372 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2373 disconnect_chain (playsink->audiochain, playsink);
2374 playsink->audiochain->volume = NULL;
2375 playsink->audiochain->mute = NULL;
2376 if (playsink->audiochain->ts_offset)
2377 gst_object_unref (playsink->audiochain->ts_offset);
2378 playsink->audiochain->ts_offset = NULL;
2379 free_chain ((GstPlayChain *) playsink->audiochain);
2380 playsink->audiochain = NULL;
2381 playsink->volume_changed = playsink->mute_changed = FALSE;
2385 if (!playsink->audiochain) {
2386 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2387 playsink->audiochain = gen_audio_chain (playsink, raw);
2390 if (!playsink->audio_sinkpad_stream_synchronizer) {
2391 GValue item = { 0, };
2394 playsink->audio_sinkpad_stream_synchronizer =
2395 gst_element_get_request_pad (GST_ELEMENT_CAST
2396 (playsink->stream_synchronizer), "sink_%d");
2397 it = gst_pad_iterate_internal_links
2398 (playsink->audio_sinkpad_stream_synchronizer);
2400 gst_iterator_next (it, &item);
2401 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
2402 g_value_unset (&item);
2403 g_assert (playsink->audio_srcpad_stream_synchronizer);
2404 gst_iterator_free (it);
2407 if (playsink->audiochain) {
2408 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2409 if (playsink->audio_tee_asrc == NULL) {
2410 playsink->audio_tee_asrc =
2411 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2413 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2414 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2415 gst_pad_link_full (playsink->audio_tee_asrc,
2416 playsink->audio_sinkpad_stream_synchronizer,
2417 GST_PAD_LINK_CHECK_NOTHING);
2418 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2419 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2422 GST_DEBUG_OBJECT (playsink, "no audio needed");
2423 /* we have no audio or we are requested to not play audio */
2424 if (playsink->audiochain) {
2425 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2426 /* release the audio pad */
2427 if (playsink->audio_tee_asrc) {
2428 gst_element_release_request_pad (playsink->audio_tee,
2429 playsink->audio_tee_asrc);
2430 gst_object_unref (playsink->audio_tee_asrc);
2431 playsink->audio_tee_asrc = NULL;
2434 if (playsink->audio_sinkpad_stream_synchronizer) {
2435 gst_element_release_request_pad (GST_ELEMENT_CAST
2436 (playsink->stream_synchronizer),
2437 playsink->audio_sinkpad_stream_synchronizer);
2438 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2439 playsink->audio_sinkpad_stream_synchronizer = NULL;
2440 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2441 playsink->audio_srcpad_stream_synchronizer = NULL;
2444 if (playsink->audiochain->sink_volume) {
2445 disconnect_chain (playsink->audiochain, playsink);
2446 playsink->audiochain->volume = NULL;
2447 playsink->audiochain->mute = NULL;
2448 if (playsink->audiochain->ts_offset)
2449 gst_object_unref (playsink->audiochain->ts_offset);
2450 playsink->audiochain->ts_offset = NULL;
2452 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2453 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2460 if (!playsink->vischain)
2461 playsink->vischain = gen_vis_chain (playsink);
2463 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2465 if (playsink->vischain) {
2466 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2468 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2469 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2470 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2471 if (playsink->audio_tee_vissrc == NULL) {
2472 playsink->audio_tee_vissrc =
2473 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2475 gst_pad_link_full (playsink->audio_tee_vissrc,
2476 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2477 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2478 GST_PAD_LINK_CHECK_NOTHING);
2479 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2480 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2481 gst_object_unref (srcpad);
2484 GST_DEBUG_OBJECT (playsink, "no vis needed");
2485 if (playsink->vischain) {
2486 if (playsink->audio_tee_vissrc) {
2487 gst_element_release_request_pad (playsink->audio_tee,
2488 playsink->audio_tee_vissrc);
2489 gst_object_unref (playsink->audio_tee_vissrc);
2490 playsink->audio_tee_vissrc = NULL;
2492 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2493 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2494 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2499 GST_DEBUG_OBJECT (playsink, "adding text");
2500 if (!playsink->textchain) {
2501 GST_DEBUG_OBJECT (playsink, "creating text chain");
2502 playsink->textchain = gen_text_chain (playsink);
2504 if (playsink->textchain) {
2507 GST_DEBUG_OBJECT (playsink, "adding text chain");
2508 if (playsink->textchain->overlay)
2509 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2511 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2513 if (!playsink->text_sinkpad_stream_synchronizer) {
2514 GValue item = { 0, };
2516 playsink->text_sinkpad_stream_synchronizer =
2517 gst_element_get_request_pad (GST_ELEMENT_CAST
2518 (playsink->stream_synchronizer), "sink_%d");
2519 it = gst_pad_iterate_internal_links
2520 (playsink->text_sinkpad_stream_synchronizer);
2522 gst_iterator_next (it, &item);
2523 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
2524 g_value_unset (&item);
2525 g_assert (playsink->text_srcpad_stream_synchronizer);
2526 gst_iterator_free (it);
2528 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2529 playsink->text_sinkpad_stream_synchronizer);
2530 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2531 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2538 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2539 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2540 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2541 GST_PAD_LINK_CHECK_NOTHING);
2542 gst_object_unref (srcpad);
2544 if (need_deinterlace)
2545 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2546 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2548 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2549 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2551 gst_pad_link_full (playsink->textchain->srcpad,
2552 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2554 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2557 GST_DEBUG_OBJECT (playsink, "no text needed");
2558 /* we have no subtitles/text or we are requested to not show them */
2560 if (playsink->text_sinkpad_stream_synchronizer) {
2561 gst_element_release_request_pad (GST_ELEMENT_CAST
2562 (playsink->stream_synchronizer),
2563 playsink->text_sinkpad_stream_synchronizer);
2564 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2565 playsink->text_sinkpad_stream_synchronizer = NULL;
2566 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2567 playsink->text_srcpad_stream_synchronizer = NULL;
2570 if (playsink->textchain) {
2571 if (playsink->text_pad == NULL) {
2572 /* no text pad, remove the chain entirely */
2573 GST_DEBUG_OBJECT (playsink, "removing text chain");
2574 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2575 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2577 /* we have a chain and a textpad, turn the subtitles off */
2578 GST_DEBUG_OBJECT (playsink, "turning off the text");
2579 if (playsink->textchain->overlay)
2580 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2584 if (!need_video && playsink->video_pad) {
2585 if (playsink->video_sinkpad_stream_synchronizer) {
2586 gst_element_release_request_pad (GST_ELEMENT_CAST
2587 (playsink->stream_synchronizer),
2588 playsink->video_sinkpad_stream_synchronizer);
2589 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2590 playsink->video_sinkpad_stream_synchronizer = NULL;
2591 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2592 playsink->video_srcpad_stream_synchronizer = NULL;
2595 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2598 if (playsink->text_pad && !playsink->textchain)
2599 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2601 update_av_offset (playsink);
2602 do_async_done (playsink);
2603 GST_PLAY_SINK_UNLOCK (playsink);
2610 /* gen_ chain already posted error */
2611 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2612 GST_PLAY_SINK_UNLOCK (playsink);
2618 * gst_play_sink_set_flags:
2619 * @playsink: a #GstPlaySink
2620 * @flags: #GstPlayFlags
2622 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2623 * when constructing the sink pipelins.
2625 * Returns: TRUE if the flags could be configured.
2628 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2630 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2632 GST_OBJECT_LOCK (playsink);
2633 playsink->flags = flags;
2634 GST_OBJECT_UNLOCK (playsink);
2640 * gst_play_sink_get_flags:
2641 * @playsink: a #GstPlaySink
2643 * Get the flags of @playsink. That flags control the behaviour of the sink when
2644 * it constructs the sink pipelines.
2646 * Returns: the currently configured #GstPlayFlags.
2649 gst_play_sink_get_flags (GstPlaySink * playsink)
2653 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2655 GST_OBJECT_LOCK (playsink);
2656 res = playsink->flags;
2657 GST_OBJECT_UNLOCK (playsink);
2663 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2665 GstPlayTextChain *chain;
2667 GST_PLAY_SINK_LOCK (playsink);
2668 chain = (GstPlayTextChain *) playsink->textchain;
2669 g_free (playsink->font_desc);
2670 playsink->font_desc = g_strdup (desc);
2671 if (chain && chain->overlay) {
2672 g_object_set (chain->overlay, "font-desc", desc, NULL);
2674 GST_PLAY_SINK_UNLOCK (playsink);
2678 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2680 gchar *result = NULL;
2681 GstPlayTextChain *chain;
2683 GST_PLAY_SINK_LOCK (playsink);
2684 chain = (GstPlayTextChain *) playsink->textchain;
2685 if (chain && chain->overlay) {
2686 g_object_get (chain->overlay, "font-desc", &result, NULL);
2687 playsink->font_desc = g_strdup (result);
2689 result = g_strdup (playsink->font_desc);
2691 GST_PLAY_SINK_UNLOCK (playsink);
2697 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2698 const gchar * encoding)
2700 GstPlayTextChain *chain;
2702 GST_PLAY_SINK_LOCK (playsink);
2703 chain = (GstPlayTextChain *) playsink->textchain;
2704 g_free (playsink->subtitle_encoding);
2705 playsink->subtitle_encoding = g_strdup (encoding);
2706 if (chain && chain->overlay) {
2707 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2709 GST_PLAY_SINK_UNLOCK (playsink);
2713 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2715 gchar *result = NULL;
2716 GstPlayTextChain *chain;
2718 GST_PLAY_SINK_LOCK (playsink);
2719 chain = (GstPlayTextChain *) playsink->textchain;
2720 if (chain && chain->overlay) {
2721 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2722 playsink->subtitle_encoding = g_strdup (result);
2724 result = g_strdup (playsink->subtitle_encoding);
2726 GST_PLAY_SINK_UNLOCK (playsink);
2732 update_av_offset (GstPlaySink * playsink)
2735 GstPlayAudioChain *achain;
2736 GstPlayVideoChain *vchain;
2738 av_offset = playsink->av_offset;
2739 achain = (GstPlayAudioChain *) playsink->audiochain;
2740 vchain = (GstPlayVideoChain *) playsink->videochain;
2742 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2743 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2744 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2746 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2751 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2753 GST_PLAY_SINK_LOCK (playsink);
2754 playsink->av_offset = av_offset;
2755 update_av_offset (playsink);
2756 GST_PLAY_SINK_UNLOCK (playsink);
2760 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2764 GST_PLAY_SINK_LOCK (playsink);
2765 result = playsink->av_offset;
2766 GST_PLAY_SINK_UNLOCK (playsink);
2772 * gst_play_sink_get_last_frame:
2773 * @playsink: a #GstPlaySink
2775 * Get the last displayed frame from @playsink. This frame is in the native
2776 * format of the sink element, the caps on the result buffer contain the format
2777 * of the frame data.
2779 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2783 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2785 GstBuffer *result = NULL;
2786 GstPlayVideoChain *chain;
2788 GST_PLAY_SINK_LOCK (playsink);
2789 GST_DEBUG_OBJECT (playsink, "taking last frame");
2790 /* get the video chain if we can */
2791 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2792 GST_DEBUG_OBJECT (playsink, "found video chain");
2793 /* see if the chain is active */
2794 if (chain->chain.activated && chain->sink) {
2797 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2799 /* find and get the last-buffer property now */
2801 gst_play_sink_find_property (playsink, chain->sink,
2802 "last-buffer", GST_TYPE_BUFFER))) {
2803 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2804 g_object_get (elem, "last-buffer", &result, NULL);
2805 gst_object_unref (elem);
2809 GST_PLAY_SINK_UNLOCK (playsink);
2815 * gst_play_sink_convert_frame:
2816 * @playsink: a #GstPlaySink
2819 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2820 * be in the native format of the sink element and the caps on the buffer
2821 * describe the format of the frame. If @caps is not %NULL, the video
2822 * frame will be converted to the format of the caps.
2824 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2825 * available or when the conversion failed.
2828 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2832 result = gst_play_sink_get_last_frame (playsink);
2833 if (result != NULL && caps != NULL) {
2837 /* FIXME, need to get the input buffer caps */
2838 temp = gst_video_convert_frame (result, NULL, caps, 25 * GST_SECOND, &err);
2839 gst_buffer_unref (result);
2840 if (temp == NULL && err) {
2841 /* I'm really uncertain whether we should make playsink post an error
2842 * on the bus or not. It's not like it's a critical issue regarding
2843 * playsink behaviour. */
2844 GST_ERROR ("Error converting frame: %s", err->message);
2852 is_raw_structure (GstStructure * s)
2856 name = gst_structure_get_name (s);
2858 if (g_str_has_prefix (name, "video/x-raw") ||
2859 g_str_has_prefix (name, "audio/x-raw"))
2865 is_raw_pad (GstPad * pad)
2867 GstPad *peer = gst_pad_get_peer (pad);
2869 gboolean raw = TRUE;
2874 caps = gst_pad_get_current_caps (peer);
2878 caps = gst_pad_get_caps (peer, NULL);
2880 n = gst_caps_get_size (caps);
2881 for (i = 0; i < n; i++) {
2882 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2886 } else if (raw != r) {
2887 GST_ERROR_OBJECT (pad,
2888 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2894 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2896 gst_caps_unref (caps);
2897 gst_object_unref (peer);
2902 static GstProbeReturn
2903 sinkpad_blocked_cb (GstPad * blockedpad, GstProbeType type, gpointer type_data,
2904 gpointer user_data);
2907 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
2909 if (playsink->video_pad) {
2911 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2912 (playsink->video_pad)));
2913 if (blocked && playsink->video_block_id == 0) {
2914 playsink->video_block_id =
2915 gst_pad_add_probe (opad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
2916 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2917 } else if (!blocked && playsink->video_block_id) {
2918 gst_pad_remove_probe (opad, playsink->video_block_id);
2919 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2920 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2921 playsink->video_block_id = 0;
2922 playsink->video_pad_blocked = FALSE;
2924 gst_object_unref (opad);
2929 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
2931 if (playsink->audio_pad) {
2933 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2934 (playsink->audio_pad)));
2935 if (blocked && playsink->audio_block_id == 0) {
2936 playsink->audio_block_id =
2937 gst_pad_add_probe (opad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
2938 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2939 } else if (!blocked && playsink->audio_block_id) {
2940 gst_pad_remove_probe (opad, playsink->audio_block_id);
2941 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2942 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2943 playsink->audio_block_id = 0;
2944 playsink->audio_pad_blocked = FALSE;
2946 gst_object_unref (opad);
2951 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
2953 if (playsink->text_pad) {
2955 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2956 (playsink->text_pad)));
2957 if (blocked && playsink->text_block_id == 0) {
2958 playsink->text_block_id =
2959 gst_pad_add_probe (opad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
2960 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2961 } else if (!blocked && playsink->text_block_id) {
2962 gst_pad_remove_probe (opad, playsink->text_block_id);
2963 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
2964 playsink->text_block_id = 0;
2965 playsink->text_pad_blocked = FALSE;
2967 gst_object_unref (opad);
2971 static GstProbeReturn
2972 sinkpad_blocked_cb (GstPad * blockedpad, GstProbeType type, gpointer type_data,
2975 GstPlaySink *playsink = (GstPlaySink *) user_data;
2978 GST_PLAY_SINK_LOCK (playsink);
2980 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2981 if (pad == playsink->video_pad) {
2982 playsink->video_pad_blocked = TRUE;
2983 GST_DEBUG_OBJECT (pad, "Video pad blocked");
2984 } else if (pad == playsink->audio_pad) {
2985 playsink->audio_pad_blocked = TRUE;
2986 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
2987 } else if (pad == playsink->text_pad) {
2988 playsink->text_pad_blocked = TRUE;
2989 GST_DEBUG_OBJECT (pad, "Text pad blocked");
2992 /* We reconfigure when for ALL streams:
2993 * * there isn't a pad
2994 * * OR the pad is blocked
2995 * * OR there are no pending blocks on that pad
2998 if ((!playsink->video_pad || playsink->video_pad_blocked
2999 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3000 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3001 && (!playsink->text_pad || playsink->text_pad_blocked
3002 || !PENDING_TEXT_BLOCK (playsink))) {
3003 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3005 if (playsink->video_pad) {
3006 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3007 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3008 playsink->video_pad_raw);
3011 if (playsink->audio_pad) {
3012 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3013 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3014 playsink->audio_pad_raw);
3017 gst_play_sink_reconfigure (playsink);
3019 video_set_blocked (playsink, FALSE);
3020 audio_set_blocked (playsink, FALSE);
3021 text_set_blocked (playsink, FALSE);
3024 gst_object_unref (pad);
3026 GST_PLAY_SINK_UNLOCK (playsink);
3028 return GST_PROBE_OK;
3032 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3034 gboolean reconfigure = FALSE;
3038 g_object_get (pad, "caps", &caps, NULL);
3042 if (pad == playsink->audio_pad) {
3043 raw = is_raw_pad (pad);
3044 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3045 && playsink->audiochain;
3046 GST_DEBUG_OBJECT (pad,
3047 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3049 } else if (pad == playsink->video_pad) {
3050 raw = is_raw_pad (pad);
3051 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3052 && playsink->videochain;
3053 GST_DEBUG_OBJECT (pad,
3054 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3058 gst_caps_unref (caps);
3061 GST_PLAY_SINK_LOCK (playsink);
3062 video_set_blocked (playsink, TRUE);
3063 audio_set_blocked (playsink, TRUE);
3064 text_set_blocked (playsink, TRUE);
3065 GST_PLAY_SINK_UNLOCK (playsink);
3070 * gst_play_sink_request_pad
3071 * @playsink: a #GstPlaySink
3072 * @type: a #GstPlaySinkType
3074 * Create or return a pad of @type.
3076 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3079 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3082 gboolean created = FALSE;
3083 gboolean activate = TRUE;
3084 const gchar *pad_name = NULL;
3085 gulong *block_id = NULL;
3087 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3089 GST_PLAY_SINK_LOCK (playsink);
3091 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3092 case GST_PLAY_SINK_TYPE_AUDIO:
3093 pad_name = "audio_sink";
3094 if (!playsink->audio_tee) {
3095 GST_LOG_OBJECT (playsink, "creating tee");
3096 /* create tee when needed. This element will feed the audio sink chain
3097 * and the vis chain. */
3098 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3099 if (playsink->audio_tee == NULL) {
3100 post_missing_element_message (playsink, "tee");
3101 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3102 (_("Missing element '%s' - check your GStreamer installation."),
3107 playsink->audio_tee_sink =
3108 gst_element_get_static_pad (playsink->audio_tee, "sink");
3109 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3110 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3113 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3115 if (!playsink->audio_pad) {
3116 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3117 playsink->audio_pad =
3118 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3119 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3120 G_CALLBACK (caps_notify_cb), playsink);
3123 playsink->audio_pad_raw = FALSE;
3124 res = playsink->audio_pad;
3125 block_id = &playsink->audio_block_id;
3127 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3128 case GST_PLAY_SINK_TYPE_VIDEO:
3129 pad_name = "video_sink";
3130 if (!playsink->video_pad) {
3131 GST_LOG_OBJECT (playsink, "ghosting videosink");
3132 playsink->video_pad =
3133 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3134 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3135 G_CALLBACK (caps_notify_cb), playsink);
3138 playsink->video_pad_raw = FALSE;
3139 res = playsink->video_pad;
3140 block_id = &playsink->video_block_id;
3142 case GST_PLAY_SINK_TYPE_TEXT:
3143 GST_LOG_OBJECT (playsink, "ghosting text");
3144 if (!playsink->text_pad) {
3145 playsink->text_pad =
3146 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3149 res = playsink->text_pad;
3150 block_id = &playsink->text_block_id;
3152 case GST_PLAY_SINK_TYPE_FLUSHING:
3156 /* we need a unique padname for the flushing pad. */
3157 padname = g_strdup_printf ("flushing_%d", playsink->count);
3158 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3169 GST_PLAY_SINK_UNLOCK (playsink);
3171 if (created && res) {
3172 /* we have to add the pad when it's active or we get an error when the
3173 * element is 'running' */
3174 gst_pad_set_active (res, TRUE);
3175 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3176 if (block_id && *block_id == 0) {
3178 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3181 gst_pad_add_probe (blockpad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
3182 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3183 PENDING_FLAG_SET (playsink, type);
3184 gst_object_unref (blockpad);
3187 gst_pad_set_active (res, activate);
3194 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3195 const gchar * name, const GstCaps * caps)
3199 GstPlaySinkType type;
3200 const gchar *tplname;
3202 g_return_val_if_fail (templ != NULL, NULL);
3204 GST_DEBUG_OBJECT (element, "name:%s", name);
3206 psink = GST_PLAY_SINK (element);
3207 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3209 /* Figure out the GstPlaySinkType based on the template */
3210 if (!strcmp (tplname, "audio_sink"))
3211 type = GST_PLAY_SINK_TYPE_AUDIO;
3212 else if (!strcmp (tplname, "audio_raw_sink"))
3213 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3214 else if (!strcmp (tplname, "video_sink"))
3215 type = GST_PLAY_SINK_TYPE_VIDEO;
3216 else if (!strcmp (tplname, "video_raw_sink"))
3217 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3218 else if (!strcmp (tplname, "text_sink"))
3219 type = GST_PLAY_SINK_TYPE_TEXT;
3221 goto unknown_template;
3223 pad = gst_play_sink_request_pad (psink, type);
3227 GST_WARNING_OBJECT (element, "Unknown pad template");
3232 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3234 GstPad **res = NULL;
3235 gboolean untarget = TRUE;
3237 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3239 GST_PLAY_SINK_LOCK (playsink);
3240 if (pad == playsink->video_pad) {
3241 res = &playsink->video_pad;
3242 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3244 } else if (pad == playsink->audio_pad) {
3245 res = &playsink->audio_pad;
3246 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3248 } else if (pad == playsink->text_pad) {
3249 res = &playsink->text_pad;
3251 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3255 GST_PLAY_SINK_UNLOCK (playsink);
3258 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3259 gst_pad_set_active (*res, FALSE);
3261 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3262 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3264 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3265 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3271 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3273 GstPlaySink *psink = GST_PLAY_SINK (element);
3275 gst_play_sink_release_pad (psink, pad);
3279 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3281 GstPlaySink *playsink;
3283 playsink = GST_PLAY_SINK_CAST (bin);
3285 switch (GST_MESSAGE_TYPE (message)) {
3286 case GST_MESSAGE_STEP_DONE:
3291 gboolean flush, intermediate, eos;
3294 GST_INFO_OBJECT (playsink, "Handling step-done message");
3295 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3296 &intermediate, &duration, &eos);
3298 if (format == GST_FORMAT_BUFFERS) {
3299 /* for the buffer format, we align the other streams */
3300 if (playsink->audiochain) {
3304 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3307 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3308 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3312 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3316 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3321 /* Send an event to our sinks until one of them works; don't then send to the
3322 * remaining sinks (unlike GstBin)
3323 * Special case: If a text sink is set we need to send the event
3324 * to them in case it's source is different from the a/v stream's source.
3327 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3329 gboolean res = TRUE;
3331 if (playsink->textchain && playsink->textchain->sink) {
3332 gst_event_ref (event);
3333 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3334 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to text sink");
3336 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3340 if (playsink->videochain) {
3341 gst_event_ref (event);
3342 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3343 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
3346 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3348 if (playsink->audiochain) {
3349 gst_event_ref (event);
3350 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3351 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
3354 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3358 gst_event_unref (event);
3362 /* We only want to send the event to a single sink (overriding GstBin's
3363 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3364 * events appropriately. So, this is a messy duplication of code. */
3366 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3368 gboolean res = FALSE;
3369 GstEventType event_type = GST_EVENT_TYPE (event);
3370 GstPlaySink *playsink;
3372 playsink = GST_PLAY_SINK_CAST (element);
3374 switch (event_type) {
3375 case GST_EVENT_SEEK:
3376 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3377 res = gst_play_sink_send_event_to_sink (playsink, event);
3379 case GST_EVENT_STEP:
3384 gboolean flush, intermediate;
3386 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3389 if (format == GST_FORMAT_BUFFERS) {
3390 /* for buffers, we will try to step video frames, for other formats we
3391 * send the step to all sinks */
3392 res = gst_play_sink_send_event_to_sink (playsink, event);
3395 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3402 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3409 static GstStateChangeReturn
3410 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3412 GstStateChangeReturn ret;
3413 GstStateChangeReturn bret;
3415 GstPlaySink *playsink;
3417 playsink = GST_PLAY_SINK (element);
3419 switch (transition) {
3420 case GST_STATE_CHANGE_READY_TO_PAUSED:
3421 playsink->need_async_start = TRUE;
3422 /* we want to go async to PAUSED until we managed to configure and add the
3424 do_async_start (playsink);
3425 ret = GST_STATE_CHANGE_ASYNC;
3427 case GST_STATE_CHANGE_PAUSED_TO_READY:
3428 /* unblock all pads here */
3429 GST_PLAY_SINK_LOCK (playsink);
3430 video_set_blocked (playsink, FALSE);
3431 audio_set_blocked (playsink, FALSE);
3432 text_set_blocked (playsink, FALSE);
3433 GST_PLAY_SINK_UNLOCK (playsink);
3435 case GST_STATE_CHANGE_READY_TO_NULL:
3436 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3437 /* remove our links to the mute and volume elements when they were
3438 * provided by a sink */
3439 disconnect_chain (playsink->audiochain, playsink);
3440 playsink->audiochain->volume = NULL;
3441 playsink->audiochain->mute = NULL;
3444 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3445 gst_object_unref (playsink->audiochain->ts_offset);
3446 playsink->audiochain->ts_offset = NULL;
3449 if (playsink->videochain && playsink->videochain->ts_offset) {
3450 gst_object_unref (playsink->videochain->ts_offset);
3451 playsink->videochain->ts_offset = NULL;
3453 ret = GST_STATE_CHANGE_SUCCESS;
3456 /* all other state changes return SUCCESS by default, this value can be
3457 * overridden by the result of the children */
3458 ret = GST_STATE_CHANGE_SUCCESS;
3462 /* do the state change of the children */
3464 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3466 /* now look at the result of our children and adjust the return value */
3468 case GST_STATE_CHANGE_FAILURE:
3469 /* failure, we stop */
3470 goto activate_failed;
3471 case GST_STATE_CHANGE_NO_PREROLL:
3472 /* some child returned NO_PREROLL. This is strange but we never know. We
3473 * commit our async state change (if any) and return the NO_PREROLL */
3474 do_async_done (playsink);
3477 case GST_STATE_CHANGE_ASYNC:
3478 /* some child was async, return this */
3482 /* return our previously configured return value */
3486 switch (transition) {
3487 case GST_STATE_CHANGE_READY_TO_PAUSED:
3489 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3490 /* FIXME Release audio device when we implement that */
3491 playsink->need_async_start = TRUE;
3493 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3494 if (playsink->video_sinkpad_stream_synchronizer) {
3495 gst_element_release_request_pad (GST_ELEMENT_CAST
3496 (playsink->stream_synchronizer),
3497 playsink->video_sinkpad_stream_synchronizer);
3498 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3499 playsink->video_sinkpad_stream_synchronizer = NULL;
3500 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3501 playsink->video_srcpad_stream_synchronizer = NULL;
3503 if (playsink->audio_sinkpad_stream_synchronizer) {
3504 gst_element_release_request_pad (GST_ELEMENT_CAST
3505 (playsink->stream_synchronizer),
3506 playsink->audio_sinkpad_stream_synchronizer);
3507 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3508 playsink->audio_sinkpad_stream_synchronizer = NULL;
3509 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3510 playsink->audio_srcpad_stream_synchronizer = NULL;
3512 if (playsink->text_sinkpad_stream_synchronizer) {
3513 gst_element_release_request_pad (GST_ELEMENT_CAST
3514 (playsink->stream_synchronizer),
3515 playsink->text_sinkpad_stream_synchronizer);
3516 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3517 playsink->text_sinkpad_stream_synchronizer = NULL;
3518 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3519 playsink->text_srcpad_stream_synchronizer = NULL;
3523 case GST_STATE_CHANGE_READY_TO_NULL:
3524 /* remove sinks we added */
3525 if (playsink->videodeinterlacechain) {
3526 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3528 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3530 if (playsink->videochain) {
3531 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3532 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3534 if (playsink->audiochain) {
3535 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3536 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3538 if (playsink->vischain) {
3539 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3540 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3542 if (playsink->textchain) {
3543 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3544 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3546 do_async_done (playsink);
3547 /* when going to READY, keep elements around as long as possible,
3548 * so they may be re-used faster next time/url around.
3549 * when really going to NULL, clean up everything completely. */
3550 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3552 /* Unparent the sinks to allow reuse */
3553 if (playsink->videochain && playsink->videochain->sink)
3554 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3555 playsink->videochain->sink);
3556 if (playsink->audiochain && playsink->audiochain->sink)
3557 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3558 playsink->audiochain->sink);
3559 if (playsink->textchain && playsink->textchain->sink)
3560 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3561 playsink->textchain->sink);
3563 if (playsink->audio_sink != NULL)
3564 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3565 if (playsink->video_sink != NULL)
3566 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3567 if (playsink->visualisation != NULL)
3568 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3569 if (playsink->text_sink != NULL)
3570 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3572 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3573 playsink->videodeinterlacechain = NULL;
3574 free_chain ((GstPlayChain *) playsink->videochain);
3575 playsink->videochain = NULL;
3576 free_chain ((GstPlayChain *) playsink->audiochain);
3577 playsink->audiochain = NULL;
3578 free_chain ((GstPlayChain *) playsink->vischain);
3579 playsink->vischain = NULL;
3580 free_chain ((GstPlayChain *) playsink->textchain);
3581 playsink->textchain = NULL;
3592 GST_DEBUG_OBJECT (element,
3593 "element failed to change states -- activation problem?");
3594 return GST_STATE_CHANGE_FAILURE;
3599 gst_play_sink_set_property (GObject * object, guint prop_id,
3600 const GValue * value, GParamSpec * spec)
3602 GstPlaySink *playsink = GST_PLAY_SINK (object);
3606 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3609 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3612 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3614 case PROP_FONT_DESC:
3615 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3617 case PROP_SUBTITLE_ENCODING:
3618 gst_play_sink_set_subtitle_encoding (playsink,
3619 g_value_get_string (value));
3621 case PROP_VIS_PLUGIN:
3622 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3624 case PROP_AV_OFFSET:
3625 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3627 case PROP_VIDEO_SINK:
3628 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3629 g_value_get_object (value));
3632 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3638 gst_play_sink_get_property (GObject * object, guint prop_id,
3639 GValue * value, GParamSpec * spec)
3641 GstPlaySink *playsink = GST_PLAY_SINK (object);
3645 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3648 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3651 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3653 case PROP_FONT_DESC:
3654 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3656 case PROP_SUBTITLE_ENCODING:
3657 g_value_take_string (value,
3658 gst_play_sink_get_subtitle_encoding (playsink));
3660 case PROP_VIS_PLUGIN:
3661 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3664 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3666 case PROP_AV_OFFSET:
3667 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3669 case PROP_VIDEO_SINK:
3670 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3671 GST_PLAY_SINK_TYPE_VIDEO));
3674 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3681 gst_play_sink_plugin_init (GstPlugin * plugin)
3683 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3685 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3686 GST_TYPE_PLAY_SINK);