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)); \
134 GStaticRecMutex lock;
136 gboolean async_pending;
137 gboolean need_async_start;
141 GstStreamSynchronizer *stream_synchronizer;
144 GstPlayAudioChain *audiochain;
145 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
146 GstPlayVideoChain *videochain;
147 GstPlayVisChain *vischain;
148 GstPlayTextChain *textchain;
152 gboolean audio_pad_raw;
153 gboolean audio_pad_blocked;
154 GstPad *audio_srcpad_stream_synchronizer;
155 GstPad *audio_sinkpad_stream_synchronizer;
156 gulong audio_block_id;
158 GstElement *audio_tee;
159 GstPad *audio_tee_sink;
160 GstPad *audio_tee_asrc;
161 GstPad *audio_tee_vissrc;
164 gboolean video_pad_raw;
165 gboolean video_pad_blocked;
166 GstPad *video_srcpad_stream_synchronizer;
167 GstPad *video_sinkpad_stream_synchronizer;
168 gulong video_block_id;
171 gboolean text_pad_blocked;
172 GstPad *text_srcpad_stream_synchronizer;
173 GstPad *text_sinkpad_stream_synchronizer;
174 gulong text_block_id;
177 GstElement *audio_sink;
178 GstElement *video_sink;
179 GstElement *visualisation;
180 GstElement *text_sink;
183 gchar *font_desc; /* font description */
184 gchar *subtitle_encoding; /* subtitle encoding */
185 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
187 gboolean volume_changed; /* volume/mute changed while no audiochain */
188 gboolean mute_changed; /* ... has been created yet */
192 struct _GstPlaySinkClass
194 GstBinClass parent_class;
196 gboolean (*reconfigure) (GstPlaySink * playsink);
198 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
202 static GstStaticPadTemplate audiotemplate =
203 GST_STATIC_PAD_TEMPLATE ("audio_sink",
206 GST_STATIC_CAPS_ANY);
207 static GstStaticPadTemplate videotemplate =
208 GST_STATIC_PAD_TEMPLATE ("video_sink",
211 GST_STATIC_CAPS_ANY);
212 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
215 GST_STATIC_CAPS_ANY);
217 /* FIXME 0.11: Remove */
218 static GstStaticPadTemplate audiorawtemplate =
219 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
222 GST_STATIC_CAPS_ANY);
223 static GstStaticPadTemplate videorawtemplate =
224 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
227 GST_STATIC_CAPS_ANY);
238 PROP_SUBTITLE_ENCODING,
251 static void gst_play_sink_dispose (GObject * object);
252 static void gst_play_sink_finalize (GObject * object);
253 static void gst_play_sink_set_property (GObject * object, guint prop_id,
254 const GValue * value, GParamSpec * spec);
255 static void gst_play_sink_get_property (GObject * object, guint prop_id,
256 GValue * value, GParamSpec * spec);
258 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
259 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
260 static void gst_play_sink_release_request_pad (GstElement * element,
262 static gboolean gst_play_sink_send_event (GstElement * element,
264 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
265 GstStateChange transition);
267 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
269 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
270 GstPlaySink * playsink);
271 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
272 GstPlaySink * playsink);
274 static void update_av_offset (GstPlaySink * playsink);
277 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
278 GValue * return_value G_GNUC_UNUSED,
279 guint n_param_values,
280 const GValue * param_values,
281 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
283 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
284 gpointer arg_1, gpointer data2);
285 register GMarshalFunc_OBJECT__BOXED callback;
286 register GCClosure *cc = (GCClosure *) closure;
287 register gpointer data1, data2;
289 g_return_if_fail (return_value != NULL);
290 g_return_if_fail (n_param_values == 2);
292 if (G_CCLOSURE_SWAP_DATA (closure)) {
293 data1 = closure->data;
294 data2 = g_value_peek_pointer (param_values + 0);
296 data1 = g_value_peek_pointer (param_values + 0);
297 data2 = closure->data;
300 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
302 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
304 gst_value_take_buffer (return_value, v_return);
307 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
309 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
312 gst_play_sink_class_init (GstPlaySinkClass * klass)
314 GObjectClass *gobject_klass;
315 GstElementClass *gstelement_klass;
316 GstBinClass *gstbin_klass;
318 gobject_klass = (GObjectClass *) klass;
319 gstelement_klass = (GstElementClass *) klass;
320 gstbin_klass = (GstBinClass *) klass;
322 gobject_klass->dispose = gst_play_sink_dispose;
323 gobject_klass->finalize = gst_play_sink_finalize;
324 gobject_klass->set_property = gst_play_sink_set_property;
325 gobject_klass->get_property = gst_play_sink_get_property;
331 * Control the behaviour of playsink.
333 g_object_class_install_property (gobject_klass, PROP_FLAGS,
334 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
335 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
336 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
339 * GstPlaySink:volume:
341 * Get or set the current audio stream volume. 1.0 means 100%,
342 * 0.0 means mute. This uses a linear volume scale.
345 g_object_class_install_property (gobject_klass, PROP_VOLUME,
346 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
347 0.0, VOLUME_MAX_DOUBLE, 1.0,
348 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
349 g_object_class_install_property (gobject_klass, PROP_MUTE,
350 g_param_spec_boolean ("mute", "Mute",
351 "Mute the audio channel without changing the volume", FALSE,
352 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
353 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
354 g_param_spec_string ("subtitle-font-desc",
355 "Subtitle font description",
356 "Pango font description of font "
357 "to be used for subtitle rendering", NULL,
358 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
359 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
360 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
361 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
362 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
363 "be checked for an encoding to use. If that is not set either, "
364 "ISO-8859-15 will be assumed.", NULL,
365 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
366 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
367 g_param_spec_object ("vis-plugin", "Vis plugin",
368 "the visualization element to use (NULL = default)",
369 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
373 * Get the currently rendered or prerolled frame in the video sink.
374 * The #GstCaps on the buffer will describe the format of the buffer.
378 g_object_class_install_property (gobject_klass, PROP_FRAME,
379 g_param_spec_boxed ("frame", "Frame",
380 "The last frame (NULL = no video available)",
381 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
383 * GstPlaySink:av-offset:
385 * Control the synchronisation offset between the audio and video streams.
386 * Positive values make the audio ahead of the video and negative values make
387 * the audio go behind the video.
391 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
392 g_param_spec_int64 ("av-offset", "AV Offset",
393 "The synchronisation offset between audio and video in nanoseconds",
394 G_MININT64, G_MAXINT64, 0,
395 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
397 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
398 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
399 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
402 * GstPlaySink::convert-frame
403 * @playsink: a #GstPlaySink
404 * @caps: the target format of the frame
406 * Action signal to retrieve the currently playing video frame in the format
407 * specified by @caps.
408 * If @caps is %NULL, no conversion will be performed and this function is
409 * equivalent to the #GstPlaySink::frame property.
411 * Returns: a #GstBuffer of the current video frame converted to #caps.
412 * The caps on the buffer will describe the final layout of the buffer data.
413 * %NULL is returned when no current buffer can be retrieved or when the
418 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
419 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
420 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
421 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
423 gst_element_class_add_pad_template (gstelement_klass,
424 gst_static_pad_template_get (&audiorawtemplate));
425 gst_element_class_add_pad_template (gstelement_klass,
426 gst_static_pad_template_get (&audiotemplate));
427 gst_element_class_add_pad_template (gstelement_klass,
428 gst_static_pad_template_get (&videorawtemplate));
429 gst_element_class_add_pad_template (gstelement_klass,
430 gst_static_pad_template_get (&videotemplate));
431 gst_element_class_add_pad_template (gstelement_klass,
432 gst_static_pad_template_get (&texttemplate));
433 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
435 "Convenience sink for multiple streams",
436 "Wim Taymans <wim.taymans@gmail.com>");
438 gstelement_klass->change_state =
439 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
440 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
441 gstelement_klass->request_new_pad =
442 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
443 gstelement_klass->release_pad =
444 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
446 gstbin_klass->handle_message =
447 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
449 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
450 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
454 gst_play_sink_init (GstPlaySink * playsink)
457 playsink->video_sink = NULL;
458 playsink->audio_sink = NULL;
459 playsink->visualisation = NULL;
460 playsink->text_sink = NULL;
461 playsink->volume = 1.0;
462 playsink->font_desc = NULL;
463 playsink->subtitle_encoding = NULL;
464 playsink->flags = DEFAULT_FLAGS;
466 playsink->stream_synchronizer =
467 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
468 gst_bin_add (GST_BIN_CAST (playsink),
469 GST_ELEMENT_CAST (playsink->stream_synchronizer));
471 g_static_rec_mutex_init (&playsink->lock);
472 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
476 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
480 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
483 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
489 free_chain (GstPlayChain * chain)
493 gst_object_unref (chain->bin);
499 gst_play_sink_dispose (GObject * object)
501 GstPlaySink *playsink;
503 playsink = GST_PLAY_SINK (object);
505 if (playsink->audio_sink != NULL) {
506 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
507 gst_object_unref (playsink->audio_sink);
508 playsink->audio_sink = NULL;
510 if (playsink->video_sink != NULL) {
511 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
512 gst_object_unref (playsink->video_sink);
513 playsink->video_sink = NULL;
515 if (playsink->visualisation != NULL) {
516 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
517 gst_object_unref (playsink->visualisation);
518 playsink->visualisation = NULL;
520 if (playsink->text_sink != NULL) {
521 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
522 gst_object_unref (playsink->text_sink);
523 playsink->text_sink = NULL;
526 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
527 playsink->videodeinterlacechain = NULL;
528 free_chain ((GstPlayChain *) playsink->videochain);
529 playsink->videochain = NULL;
530 free_chain ((GstPlayChain *) playsink->audiochain);
531 playsink->audiochain = NULL;
532 free_chain ((GstPlayChain *) playsink->vischain);
533 playsink->vischain = NULL;
534 free_chain ((GstPlayChain *) playsink->textchain);
535 playsink->textchain = NULL;
537 if (playsink->audio_tee_sink) {
538 gst_object_unref (playsink->audio_tee_sink);
539 playsink->audio_tee_sink = NULL;
542 if (playsink->audio_tee_vissrc) {
543 gst_element_release_request_pad (playsink->audio_tee,
544 playsink->audio_tee_vissrc);
545 gst_object_unref (playsink->audio_tee_vissrc);
546 playsink->audio_tee_vissrc = NULL;
549 if (playsink->audio_tee_asrc) {
550 gst_element_release_request_pad (playsink->audio_tee,
551 playsink->audio_tee_asrc);
552 gst_object_unref (playsink->audio_tee_asrc);
553 playsink->audio_tee_asrc = NULL;
556 g_free (playsink->font_desc);
557 playsink->font_desc = NULL;
559 g_free (playsink->subtitle_encoding);
560 playsink->subtitle_encoding = NULL;
562 playsink->stream_synchronizer = NULL;
564 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
568 gst_play_sink_finalize (GObject * object)
570 GstPlaySink *playsink;
572 playsink = GST_PLAY_SINK (object);
574 g_static_rec_mutex_free (&playsink->lock);
576 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
580 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
583 GstElement **elem = NULL, *old = NULL;
585 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
587 GST_PLAY_SINK_LOCK (playsink);
589 case GST_PLAY_SINK_TYPE_AUDIO:
590 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
591 elem = &playsink->audio_sink;
593 case GST_PLAY_SINK_TYPE_VIDEO:
594 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
595 elem = &playsink->video_sink;
597 case GST_PLAY_SINK_TYPE_TEXT:
598 elem = &playsink->text_sink;
606 gst_object_ref (sink);
609 GST_PLAY_SINK_UNLOCK (playsink);
613 gst_element_set_state (old, GST_STATE_NULL);
614 gst_object_unref (old);
619 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
621 GstElement *result = NULL;
622 GstElement *elem = NULL, *chainp = NULL;
624 GST_PLAY_SINK_LOCK (playsink);
626 case GST_PLAY_SINK_TYPE_AUDIO:
627 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
629 GstPlayAudioChain *chain;
630 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
631 chainp = chain->sink;
632 elem = playsink->audio_sink;
635 case GST_PLAY_SINK_TYPE_VIDEO:
636 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
638 GstPlayVideoChain *chain;
639 if ((chain = (GstPlayVideoChain *) playsink->videochain))
640 chainp = chain->sink;
641 elem = playsink->video_sink;
644 case GST_PLAY_SINK_TYPE_TEXT:
646 GstPlayTextChain *chain;
647 if ((chain = (GstPlayTextChain *) playsink->textchain))
648 chainp = chain->sink;
649 elem = playsink->text_sink;
656 /* we have an active chain with a sink, get the sink */
657 result = gst_object_ref (chainp);
659 /* nothing found, return last configured sink */
660 if (result == NULL && elem)
661 result = gst_object_ref (elem);
662 GST_PLAY_SINK_UNLOCK (playsink);
667 static GstProbeReturn
668 gst_play_sink_vis_blocked (GstPad * tee_pad, GstProbeType type,
669 gpointer type_data, gpointer user_data)
671 GstPlaySink *playsink;
672 GstPlayVisChain *chain;
674 playsink = GST_PLAY_SINK (user_data);
676 GST_PLAY_SINK_LOCK (playsink);
677 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
678 /* now try to change the plugin in the running vis chain */
679 if (!(chain = (GstPlayVisChain *) playsink->vischain))
682 /* unlink the old plugin and unghost the pad */
683 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
684 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
686 /* set the old plugin to NULL and remove */
687 gst_element_set_state (chain->vis, GST_STATE_NULL);
688 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
690 /* add new plugin and set state to playing */
691 chain->vis = playsink->visualisation;
692 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
693 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
696 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
697 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
700 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
701 GST_PAD_LINK_CHECK_NOTHING);
702 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
706 GST_PLAY_SINK_UNLOCK (playsink);
708 /* remove the probe and unblock the pad */
709 return GST_PROBE_REMOVE;
713 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
715 GstPlayVisChain *chain;
717 /* setting NULL means creating the default vis plugin */
719 vis = gst_element_factory_make ("goom", "vis");
721 /* simply return if we don't have a vis plugin here */
725 GST_PLAY_SINK_LOCK (playsink);
726 /* first store the new vis */
727 if (playsink->visualisation)
728 gst_object_unref (playsink->visualisation);
730 gst_object_ref_sink (vis);
731 playsink->visualisation = vis;
733 /* now try to change the plugin in the running vis chain, if we have no chain,
734 * we don't bother, any future vis chain will be created with the new vis
736 if (!(chain = (GstPlayVisChain *) playsink->vischain))
739 /* block the pad, the next time the callback is called we can change the
740 * visualisation. It's possible that this never happens or that the pad was
741 * already blocked. If the callback never happens, we don't have new data so
742 * we don't need the new vis plugin. If the pad was already blocked, the
743 * function returns FALSE but the previous pad block will do the right thing
745 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
746 gst_pad_add_probe (chain->blockpad, GST_PROBE_TYPE_BLOCK,
747 gst_play_sink_vis_blocked, playsink, NULL);
749 GST_PLAY_SINK_UNLOCK (playsink);
755 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
757 GstElement *result = NULL;
758 GstPlayVisChain *chain;
760 GST_PLAY_SINK_LOCK (playsink);
761 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
762 /* we have an active chain, get the sink */
764 result = gst_object_ref (chain->vis);
766 /* nothing found, return last configured sink */
767 if (result == NULL && playsink->visualisation)
768 result = gst_object_ref (playsink->visualisation);
769 GST_PLAY_SINK_UNLOCK (playsink);
775 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
777 GstPlayAudioChain *chain;
779 GST_PLAY_SINK_LOCK (playsink);
780 playsink->volume = volume;
781 chain = (GstPlayAudioChain *) playsink->audiochain;
782 if (chain && chain->volume) {
783 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
784 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
785 chain->mute, volume, playsink->mute);
786 /* if there is a mute element or we are not muted, set the volume */
787 if (chain->mute || !playsink->mute)
788 g_object_set (chain->volume, "volume", volume, NULL);
790 GST_LOG_OBJECT (playsink, "no volume element");
791 playsink->volume_changed = TRUE;
793 GST_PLAY_SINK_UNLOCK (playsink);
797 gst_play_sink_get_volume (GstPlaySink * playsink)
800 GstPlayAudioChain *chain;
802 GST_PLAY_SINK_LOCK (playsink);
803 chain = (GstPlayAudioChain *) playsink->audiochain;
804 result = playsink->volume;
805 if (chain && chain->volume) {
806 if (chain->mute || !playsink->mute) {
807 g_object_get (chain->volume, "volume", &result, NULL);
808 playsink->volume = result;
811 GST_PLAY_SINK_UNLOCK (playsink);
817 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
819 GstPlayAudioChain *chain;
821 GST_PLAY_SINK_LOCK (playsink);
822 playsink->mute = mute;
823 chain = (GstPlayAudioChain *) playsink->audiochain;
826 g_object_set (chain->mute, "mute", mute, NULL);
827 } else if (chain->volume) {
829 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
831 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
835 playsink->mute_changed = TRUE;
837 GST_PLAY_SINK_UNLOCK (playsink);
841 gst_play_sink_get_mute (GstPlaySink * playsink)
844 GstPlayAudioChain *chain;
846 GST_PLAY_SINK_LOCK (playsink);
847 chain = (GstPlayAudioChain *) playsink->audiochain;
848 if (chain && chain->mute) {
849 g_object_get (chain->mute, "mute", &result, NULL);
850 playsink->mute = result;
852 result = playsink->mute;
854 GST_PLAY_SINK_UNLOCK (playsink);
860 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
864 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
865 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
869 add_chain (GstPlayChain * chain, gboolean add)
871 if (chain->added == add)
875 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
877 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
878 /* we don't want to lose our sink status */
879 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
888 activate_chain (GstPlayChain * chain, gboolean activate)
892 if (chain->activated == activate)
895 GST_OBJECT_LOCK (chain->playsink);
896 state = GST_STATE_TARGET (chain->playsink);
897 GST_OBJECT_UNLOCK (chain->playsink);
900 gst_element_set_state (chain->bin, state);
902 gst_element_set_state (chain->bin, GST_STATE_NULL);
904 chain->activated = activate;
910 element_is_sink (GstElement * element)
914 GST_OBJECT_LOCK (element);
915 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
916 GST_OBJECT_UNLOCK (element);
918 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
923 element_has_property (GstElement * element, const gchar * pname, GType type)
927 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
930 GST_DEBUG_OBJECT (element, "no %s property", pname);
934 if (type == G_TYPE_INVALID || type == pspec->value_type ||
935 g_type_is_a (pspec->value_type, type)) {
936 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
937 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
941 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
942 "and we expected it to be of type %s", pname,
943 g_type_name (pspec->value_type), g_type_name (type));
950 const gchar *prop_name;
953 } FindPropertyHelper;
956 find_property (const GValue * item, FindPropertyHelper * helper)
958 GstElement *element = g_value_get_object (item);
959 if (helper->need_sink && !element_is_sink (element)) {
963 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
967 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
968 (helper->need_sink) ? "sink" : "element");
969 return 0; /* keep it */
972 /* FIXME: why not move these functions into core? */
973 /* find a sink in the hierarchy with a property named @name. This function does
974 * not increase the refcount of the returned object and thus remains valid as
975 * long as the bin is valid. */
977 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
978 const gchar * name, GType expected_type)
980 GstElement *result = NULL;
983 if (element_has_property (obj, name, expected_type)) {
985 } else if (GST_IS_BIN (obj)) {
987 GValue item = { 0, };
988 FindPropertyHelper helper = { name, expected_type, TRUE };
990 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
991 found = gst_iterator_find_custom (it,
992 (GCompareFunc) find_property, &item, &helper);
993 gst_iterator_free (it);
995 result = g_value_get_object (&item);
996 /* we don't need the extra ref */
997 g_value_unset (&item);
1002 /* find an object in the hierarchy with a property named @name */
1004 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1005 const gchar * name, GType expected_type)
1007 GstElement *result = NULL;
1010 if (GST_IS_BIN (obj)) {
1012 GValue item = { 0, };
1013 FindPropertyHelper helper = { name, expected_type, FALSE };
1015 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1016 found = gst_iterator_find_custom (it,
1017 (GCompareFunc) find_property, &item, &helper);
1018 gst_iterator_free (it);
1020 result = g_value_dup_object (&item);
1021 g_value_unset (&item);
1023 if (element_has_property (obj, name, expected_type)) {
1025 gst_object_ref (obj);
1032 do_async_start (GstPlaySink * playsink)
1034 GstMessage *message;
1036 if (!playsink->need_async_start) {
1037 GST_INFO_OBJECT (playsink, "no async_start needed");
1041 playsink->async_pending = TRUE;
1043 GST_INFO_OBJECT (playsink, "Sending async_start message");
1044 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1045 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1046 (playsink), message);
1050 do_async_done (GstPlaySink * playsink)
1052 GstMessage *message;
1054 if (playsink->async_pending) {
1055 GST_INFO_OBJECT (playsink, "Sending async_done message");
1056 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1057 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1058 (playsink), message);
1060 playsink->async_pending = FALSE;
1063 playsink->need_async_start = FALSE;
1066 /* try to change the state of an element. This function returns the element when
1067 * the state change could be performed. When this function returns NULL an error
1068 * occured and the element is unreffed if @unref is TRUE. */
1070 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1072 GstStateChangeReturn ret;
1075 ret = gst_element_set_state (element, GST_STATE_READY);
1076 if (ret == GST_STATE_CHANGE_FAILURE) {
1077 GST_DEBUG_OBJECT (playsink, "failed state change..");
1078 gst_element_set_state (element, GST_STATE_NULL);
1080 gst_object_unref (element);
1087 /* make the element (bin) that contains the elements needed to perform
1088 * video display. Only used for *raw* video streams.
1090 * +------------------------------------------------------------+
1092 * | +-------+ +----------+ +----------+ +---------+ |
1093 * | | queue | |colorspace| |videoscale| |videosink| |
1094 * | +-sink src-sink src-sink src-sink | |
1095 * | | +-------+ +----------+ +----------+ +---------+ |
1097 * +------------------------------------------------------------+
1100 static GstPlayVideoDeinterlaceChain *
1101 gen_video_deinterlace_chain (GstPlaySink * playsink)
1103 GstPlayVideoDeinterlaceChain *chain;
1106 GstElement *head = NULL, *prev = NULL;
1108 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1109 chain->chain.playsink = playsink;
1111 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1113 /* create a bin to hold objects, as we create them we add them to this bin so
1114 * that when something goes wrong we only need to unref the bin */
1115 chain->chain.bin = gst_bin_new ("vdbin");
1116 bin = GST_BIN_CAST (chain->chain.bin);
1117 gst_object_ref_sink (bin);
1119 GST_DEBUG_OBJECT (playsink, "creating videoconvert");
1120 chain->conv = gst_element_factory_make ("videoconvert", "vdconv");
1121 if (chain->conv == NULL) {
1122 post_missing_element_message (playsink, "videoconvert");
1123 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1124 (_("Missing element '%s' - check your GStreamer installation."),
1125 "videoconvert"), ("video rendering might fail"));
1127 gst_bin_add (bin, chain->conv);
1132 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1133 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1134 if (chain->deinterlace == NULL) {
1135 post_missing_element_message (playsink, "deinterlace");
1136 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1137 (_("Missing element '%s' - check your GStreamer installation."),
1138 "deinterlace"), ("deinterlacing won't work"));
1140 gst_bin_add (bin, chain->deinterlace);
1142 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1143 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1146 head = chain->deinterlace;
1148 prev = chain->deinterlace;
1152 pad = gst_element_get_static_pad (head, "sink");
1153 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1154 gst_object_unref (pad);
1156 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1160 pad = gst_element_get_static_pad (prev, "src");
1161 chain->srcpad = gst_ghost_pad_new ("src", pad);
1162 gst_object_unref (pad);
1164 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1167 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1168 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1174 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1175 (NULL), ("Failed to configure the video deinterlace chain."));
1176 free_chain ((GstPlayChain *) chain);
1181 /* make the element (bin) that contains the elements needed to perform
1184 * +------------------------------------------------------------+
1186 * | +-------+ +----------+ +----------+ +---------+ |
1187 * | | queue | |colorspace| |videoscale| |videosink| |
1188 * | +-sink src-sink src-sink src-sink | |
1189 * | | +-------+ +----------+ +----------+ +---------+ |
1191 * +------------------------------------------------------------+
1194 static GstPlayVideoChain *
1195 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1197 GstPlayVideoChain *chain;
1200 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1202 chain = g_new0 (GstPlayVideoChain, 1);
1203 chain->chain.playsink = playsink;
1204 chain->chain.raw = raw;
1206 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1208 if (playsink->video_sink) {
1209 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1210 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1212 /* only try fallback if no specific sink was chosen */
1213 if (chain->sink == NULL) {
1214 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1215 elem = gst_element_factory_make ("autovideosink", "videosink");
1216 chain->sink = try_element (playsink, elem, TRUE);
1218 if (chain->sink == NULL) {
1219 /* if default sink from config.h is different then try it too */
1220 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1221 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1222 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1223 chain->sink = try_element (playsink, elem, TRUE);
1227 playsink->video_sink = gst_object_ref (chain->sink);
1229 if (chain->sink == NULL)
1233 /* if we can disable async behaviour of the sink, we can avoid adding a
1234 * queue for the audio chain. */
1236 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1239 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1240 async, GST_ELEMENT_NAME (elem));
1241 g_object_set (elem, "async", async, NULL);
1242 chain->async = async;
1244 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1245 chain->async = TRUE;
1248 /* find ts-offset element */
1250 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1253 /* create a bin to hold objects, as we create them we add them to this bin so
1254 * that when something goes wrong we only need to unref the bin */
1255 chain->chain.bin = gst_bin_new ("vbin");
1256 bin = GST_BIN_CAST (chain->chain.bin);
1257 gst_object_ref_sink (bin);
1258 gst_bin_add (bin, chain->sink);
1260 /* decouple decoder from sink, this improves playback quite a lot since the
1261 * decoder can continue while the sink blocks for synchronisation. We don't
1262 * need a lot of buffers as this consumes a lot of memory and we don't want
1263 * too little because else we would be context switching too quickly. */
1264 chain->queue = gst_element_factory_make ("queue", "vqueue");
1265 if (chain->queue == NULL) {
1266 post_missing_element_message (playsink, "queue");
1267 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1268 (_("Missing element '%s' - check your GStreamer installation."),
1269 "queue"), ("video rendering might be suboptimal"));
1273 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1274 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1275 gst_bin_add (bin, chain->queue);
1276 head = prev = chain->queue;
1279 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1280 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1282 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1283 gst_bin_add (bin, chain->conv);
1285 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1286 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1295 GST_DEBUG_OBJECT (playsink, "linking to sink");
1296 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1297 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1301 pad = gst_element_get_static_pad (head, "sink");
1302 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1303 gst_object_unref (pad);
1305 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1312 if (!elem && !playsink->video_sink) {
1313 post_missing_element_message (playsink, "autovideosink");
1314 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1315 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1316 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1317 (_("Both autovideosink and %s elements are missing."),
1318 DEFAULT_VIDEOSINK), (NULL));
1320 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1321 (_("The autovideosink element is missing.")), (NULL));
1324 if (playsink->video_sink) {
1325 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1326 (_("Configured videosink %s is not working."),
1327 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1328 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1329 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1330 (_("Both autovideosink and %s elements are not working."),
1331 DEFAULT_VIDEOSINK), (NULL));
1333 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1334 (_("The autovideosink element is not working.")), (NULL));
1337 free_chain ((GstPlayChain *) chain);
1342 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1343 (NULL), ("Failed to configure the video sink."));
1344 /* checking sink made it READY */
1345 gst_element_set_state (chain->sink, GST_STATE_NULL);
1346 /* Remove chain from the bin to allow reuse later */
1347 gst_bin_remove (bin, chain->sink);
1348 free_chain ((GstPlayChain *) chain);
1354 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1357 GstPlayVideoChain *chain;
1358 GstStateChangeReturn ret;
1360 chain = playsink->videochain;
1362 chain->chain.raw = raw;
1364 /* if the chain was active we don't do anything */
1365 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1368 /* try to set the sink element to READY again */
1369 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1370 if (ret == GST_STATE_CHANGE_FAILURE)
1373 /* find ts-offset element */
1375 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1378 /* if we can disable async behaviour of the sink, we can avoid adding a
1379 * queue for the audio chain. */
1381 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1384 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1385 async, GST_ELEMENT_NAME (elem));
1386 g_object_set (elem, "async", async, NULL);
1387 chain->async = async;
1389 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1390 chain->async = TRUE;
1395 /* make an element for playback of video with subtitles embedded.
1396 * Only used for *raw* video streams.
1398 * +--------------------------------------------+
1400 * | +--------+ +-----------------+ |
1401 * | | queue | | subtitleoverlay | |
1402 * video--src sink---video_sink | |
1403 * | +--------+ | src--src
1404 * text------------------text_sink | |
1405 * | +-----------------+ |
1406 * +--------------------------------------------+
1409 static GstPlayTextChain *
1410 gen_text_chain (GstPlaySink * playsink)
1412 GstPlayTextChain *chain;
1415 GstPad *videosinkpad, *textsinkpad, *srcpad;
1417 chain = g_new0 (GstPlayTextChain, 1);
1418 chain->chain.playsink = playsink;
1420 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1422 chain->chain.bin = gst_bin_new ("tbin");
1423 bin = GST_BIN_CAST (chain->chain.bin);
1424 gst_object_ref_sink (bin);
1426 videosinkpad = textsinkpad = srcpad = NULL;
1428 /* first try to hook the text pad to the custom sink */
1429 if (playsink->text_sink) {
1430 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1431 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1434 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1437 /* make sure the sparse subtitles don't participate in the preroll */
1438 g_object_set (elem, "async", FALSE, NULL);
1439 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1440 gst_bin_add (bin, chain->sink);
1441 /* NOTE streamsynchronizer needs streams decoupled */
1442 /* make a little queue */
1443 chain->queue = gst_element_factory_make ("queue", "subqueue");
1444 if (chain->queue == NULL) {
1445 post_missing_element_message (playsink, "queue");
1446 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1447 (_("Missing element '%s' - check your GStreamer installation."),
1448 "queue"), ("rendering might be suboptimal"));
1450 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1451 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1452 "silent", TRUE, NULL);
1453 gst_bin_add (bin, chain->queue);
1455 /* we have a custom sink, this will be our textsinkpad */
1456 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1457 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1458 /* we're all fine now and we can add the sink to the chain */
1459 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1460 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1462 GST_WARNING_OBJECT (playsink,
1463 "can't find a sink pad on custom text sink");
1464 gst_bin_remove (bin, chain->sink);
1465 gst_bin_remove (bin, chain->queue);
1467 chain->queue = NULL;
1469 /* try to set sync to true but it's no biggie when we can't */
1471 gst_play_sink_find_property_sinks (playsink, chain->sink,
1472 "sync", G_TYPE_BOOLEAN)))
1473 g_object_set (elem, "sync", TRUE, NULL);
1476 gst_bin_remove (bin, chain->sink);
1478 GST_WARNING_OBJECT (playsink,
1479 "can't find async property in custom text sink");
1482 if (textsinkpad == NULL) {
1483 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1484 (_("Custom text sink element is not usable.")),
1485 ("fallback to default textoverlay"));
1489 if (textsinkpad == NULL) {
1490 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1491 /* make a little queue */
1492 chain->queue = gst_element_factory_make ("queue", "vqueue");
1493 if (chain->queue == NULL) {
1494 post_missing_element_message (playsink, "queue");
1495 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1496 (_("Missing element '%s' - check your GStreamer installation."),
1497 "queue"), ("video rendering might be suboptimal"));
1499 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1500 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1501 "silent", TRUE, NULL);
1502 gst_bin_add (bin, chain->queue);
1503 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1507 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1508 if (chain->overlay == NULL) {
1509 post_missing_element_message (playsink, "subtitleoverlay");
1510 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1511 (_("Missing element '%s' - check your GStreamer installation."),
1512 "subtitleoverlay"), ("subtitle rendering disabled"));
1514 GstElement *element;
1516 gst_bin_add (bin, chain->overlay);
1518 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1519 if (playsink->font_desc) {
1520 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1521 playsink->font_desc, NULL);
1523 if (playsink->subtitle_encoding) {
1524 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1525 playsink->subtitle_encoding, NULL);
1528 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1529 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1531 /* make another little queue to decouple streams */
1532 element = gst_element_factory_make ("queue", "subqueue");
1533 if (element == NULL) {
1534 post_missing_element_message (playsink, "queue");
1535 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1536 (_("Missing element '%s' - check your GStreamer installation."),
1537 "queue"), ("rendering might be suboptimal"));
1539 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1540 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1541 "silent", TRUE, NULL);
1542 gst_bin_add (bin, element);
1543 gst_element_link_pads_full (element, "src", chain->overlay,
1544 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1545 textsinkpad = gst_element_get_static_pad (element, "sink");
1546 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1552 if (videosinkpad == NULL) {
1553 /* if we still don't have a videosink, we don't have an overlay. the only
1554 * thing we can do is insert an identity and ghost the src
1556 chain->identity = gst_element_factory_make ("identity", "tidentity");
1557 if (chain->identity == NULL) {
1558 post_missing_element_message (playsink, "identity");
1559 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1560 (_("Missing element '%s' - check your GStreamer installation."),
1561 "identity"), (NULL));
1563 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1564 g_object_set (chain->identity, "silent", TRUE, NULL);
1565 gst_bin_add (bin, chain->identity);
1566 srcpad = gst_element_get_static_pad (chain->identity, "src");
1567 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1571 /* expose the ghostpads */
1573 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1574 gst_object_unref (videosinkpad);
1575 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1578 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1579 gst_object_unref (textsinkpad);
1580 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1583 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1584 gst_object_unref (srcpad);
1585 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1592 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1596 g_object_get (object, "volume", &vol, NULL);
1597 playsink->volume = vol;
1599 g_object_notify (G_OBJECT (playsink), "volume");
1603 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1607 g_object_get (object, "mute", &mute, NULL);
1608 playsink->mute = mute;
1610 g_object_notify (G_OBJECT (playsink), "mute");
1613 /* make the chain that contains the elements needed to perform
1616 * We add a tee as the first element so that we can link the visualisation chain
1617 * to it when requested.
1619 * +-------------------------------------------------------------+
1621 * | +---------+ +----------+ +---------+ +---------+ |
1622 * | |audioconv| |audioscale| | volume | |audiosink| |
1623 * | +-srck src-sink src-sink src-sink | |
1624 * | | +---------+ +----------+ +---------+ +---------+ |
1626 * +-------------------------------------------------------------+
1628 static GstPlayAudioChain *
1629 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1631 GstPlayAudioChain *chain;
1633 gboolean have_volume;
1635 GstElement *head, *prev, *elem = NULL;
1637 chain = g_new0 (GstPlayAudioChain, 1);
1638 chain->chain.playsink = playsink;
1639 chain->chain.raw = raw;
1641 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1643 if (playsink->audio_sink) {
1644 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1645 playsink->audio_sink);
1646 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1648 /* only try fallback if no specific sink was chosen */
1649 if (chain->sink == NULL) {
1650 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1651 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1652 chain->sink = try_element (playsink, elem, TRUE);
1654 if (chain->sink == NULL) {
1655 /* if default sink from config.h is different then try it too */
1656 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1657 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1658 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1659 chain->sink = try_element (playsink, elem, TRUE);
1663 playsink->audio_sink = gst_object_ref (chain->sink);
1665 if (chain->sink == NULL)
1668 chain->chain.bin = gst_bin_new ("abin");
1669 bin = GST_BIN_CAST (chain->chain.bin);
1670 gst_object_ref_sink (bin);
1671 gst_bin_add (bin, chain->sink);
1673 /* we have to add a queue when we need to decouple for the video sink in
1674 * visualisations and for streamsynchronizer */
1675 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1676 chain->queue = gst_element_factory_make ("queue", "aqueue");
1677 if (chain->queue == NULL) {
1678 post_missing_element_message (playsink, "queue");
1679 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1680 (_("Missing element '%s' - check your GStreamer installation."),
1681 "queue"), ("audio playback and visualizations might not work"));
1685 g_object_set (chain->queue, "silent", TRUE, NULL);
1686 gst_bin_add (bin, chain->queue);
1687 prev = head = chain->queue;
1690 /* find ts-offset element */
1692 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1695 /* check if the sink, or something within the sink, has the volume property.
1696 * If it does we don't need to add a volume element. */
1698 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1701 chain->volume = elem;
1703 g_signal_connect (chain->volume, "notify::volume",
1704 G_CALLBACK (notify_volume_cb), playsink);
1706 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1708 chain->sink_volume = TRUE;
1709 /* if the sink also has a mute property we can use this as well. We'll only
1710 * use the mute property if there is a volume property. We can simulate the
1711 * mute with the volume otherwise. */
1713 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1716 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1717 g_signal_connect (chain->mute, "notify::mute",
1718 G_CALLBACK (notify_mute_cb), playsink);
1720 /* use the sink to control the volume and mute */
1721 if (playsink->volume_changed) {
1722 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1723 playsink->volume_changed = FALSE;
1725 if (playsink->mute_changed) {
1727 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1730 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1732 playsink->mute_changed = FALSE;
1735 /* no volume, we need to add a volume element when we can */
1736 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1737 have_volume = FALSE;
1738 chain->sink_volume = FALSE;
1741 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1742 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1743 GST_DEBUG_OBJECT (playsink, "creating audioconvert");
1745 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL);
1746 gst_bin_add (bin, chain->conv);
1748 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1749 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1756 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_converters =
1757 !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1758 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = (!have_volume
1759 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
1761 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1762 GstPlaySinkAudioConvert *conv =
1763 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1766 chain->volume = conv->volume;
1769 g_signal_connect (chain->volume, "notify::volume",
1770 G_CALLBACK (notify_volume_cb), playsink);
1772 /* volume also has the mute property */
1773 chain->mute = chain->volume;
1774 g_signal_connect (chain->mute, "notify::mute",
1775 G_CALLBACK (notify_mute_cb), playsink);
1777 /* configure with the latest volume and mute */
1778 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1780 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1786 /* we only have to link to the previous element if we have something in
1787 * front of the sink */
1788 GST_DEBUG_OBJECT (playsink, "linking to sink");
1789 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1790 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1794 /* post a warning if we have no way to configure the volume */
1796 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1797 (_("No volume control found")), ("Volume/mute is not available"));
1800 /* and ghost the sinkpad of the headmost element */
1801 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1802 pad = gst_element_get_static_pad (head, "sink");
1803 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1804 gst_object_unref (pad);
1805 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1812 if (!elem && !playsink->audio_sink) {
1813 post_missing_element_message (playsink, "autoaudiosink");
1814 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1815 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1816 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1817 (_("Both autoaudiosink and %s elements are missing."),
1818 DEFAULT_AUDIOSINK), (NULL));
1820 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1821 (_("The autoaudiosink element is missing.")), (NULL));
1824 if (playsink->audio_sink) {
1825 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1826 (_("Configured audiosink %s is not working."),
1827 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1828 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1829 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1830 (_("Both autoaudiosink and %s elements are not working."),
1831 DEFAULT_AUDIOSINK), (NULL));
1833 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1834 (_("The autoaudiosink element is not working.")), (NULL));
1837 free_chain ((GstPlayChain *) chain);
1842 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1843 (NULL), ("Failed to configure the audio sink."));
1844 /* checking sink made it READY */
1845 gst_element_set_state (chain->sink, GST_STATE_NULL);
1846 /* Remove chain from the bin to allow reuse later */
1847 gst_bin_remove (bin, chain->sink);
1848 free_chain ((GstPlayChain *) chain);
1854 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1857 GstPlayAudioChain *chain;
1858 GstStateChangeReturn ret;
1860 chain = playsink->audiochain;
1862 chain->chain.raw = raw;
1864 /* if the chain was active we don't do anything */
1865 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1868 /* try to set the sink element to READY again */
1869 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1870 if (ret == GST_STATE_CHANGE_FAILURE)
1873 /* find ts-offset element */
1875 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1878 /* check if the sink, or something within the sink, has the volume property.
1879 * If it does we don't need to add a volume element. */
1881 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1884 chain->volume = elem;
1886 if (playsink->volume_changed) {
1887 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1889 /* use the sink to control the volume */
1890 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1891 playsink->volume_changed = FALSE;
1894 g_signal_connect (chain->volume, "notify::volume",
1895 G_CALLBACK (notify_volume_cb), playsink);
1896 /* if the sink also has a mute property we can use this as well. We'll only
1897 * use the mute property if there is a volume property. We can simulate the
1898 * mute with the volume otherwise. */
1900 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1903 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1904 g_signal_connect (chain->mute, "notify::mute",
1905 G_CALLBACK (notify_mute_cb), playsink);
1908 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE;
1910 GstPlaySinkAudioConvert *conv =
1911 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1913 /* no volume, we need to add a volume element when we can */
1914 conv->use_volume = TRUE;
1915 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1917 /* Disconnect signals */
1918 disconnect_chain (chain, playsink);
1921 chain->volume = conv->volume;
1922 chain->mute = chain->volume;
1924 g_signal_connect (chain->volume, "notify::volume",
1925 G_CALLBACK (notify_volume_cb), playsink);
1927 g_signal_connect (chain->mute, "notify::mute",
1928 G_CALLBACK (notify_mute_cb), playsink);
1930 /* configure with the latest volume and mute */
1931 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1932 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1935 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
1941 * +-------------------------------------------------------------------+
1943 * | +----------+ +------------+ +----------+ +-------+ |
1944 * | | visqueue | | audioconv | | audiores | | vis | |
1945 * | +-sink src-sink + samp src-sink src-sink src-+ |
1946 * | | +----------+ +------------+ +----------+ +-------+ | |
1948 * +-------------------------------------------------------------------+
1951 static GstPlayVisChain *
1952 gen_vis_chain (GstPlaySink * playsink)
1954 GstPlayVisChain *chain;
1960 chain = g_new0 (GstPlayVisChain, 1);
1961 chain->chain.playsink = playsink;
1963 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
1965 chain->chain.bin = gst_bin_new ("visbin");
1966 bin = GST_BIN_CAST (chain->chain.bin);
1967 gst_object_ref_sink (bin);
1969 /* we're queuing raw audio here, we can remove this queue when we can disable
1970 * async behaviour in the video sink. */
1971 chain->queue = gst_element_factory_make ("queue", "visqueue");
1972 if (chain->queue == NULL)
1974 g_object_set (chain->queue, "silent", TRUE, NULL);
1975 gst_bin_add (bin, chain->queue);
1977 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1978 if (chain->conv == NULL)
1979 goto no_audioconvert;
1980 gst_bin_add (bin, chain->conv);
1982 chain->resample = gst_element_factory_make ("audioresample", "aresample");
1983 if (chain->resample == NULL)
1984 goto no_audioresample;
1985 gst_bin_add (bin, chain->resample);
1987 /* this pad will be used for blocking the dataflow and switching the vis
1989 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
1991 if (playsink->visualisation) {
1992 GST_DEBUG_OBJECT (playsink, "trying configure vis");
1993 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
1995 if (chain->vis == NULL) {
1996 GST_DEBUG_OBJECT (playsink, "trying goom");
1997 elem = gst_element_factory_make ("goom", "vis");
1998 chain->vis = try_element (playsink, elem, TRUE);
2000 if (chain->vis == NULL)
2003 gst_bin_add (bin, chain->vis);
2005 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2006 GST_PAD_LINK_CHECK_NOTHING);
2008 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2009 GST_PAD_LINK_CHECK_NOTHING);
2011 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2012 GST_PAD_LINK_CHECK_NOTHING);
2016 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2017 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2019 pad = gst_element_get_static_pad (chain->queue, "sink");
2020 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2021 gst_object_unref (pad);
2022 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2024 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2025 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2032 post_missing_element_message (playsink, "queue");
2033 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2034 (_("Missing element '%s' - check your GStreamer installation."),
2036 free_chain ((GstPlayChain *) chain);
2041 post_missing_element_message (playsink, "audioconvert");
2042 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2043 (_("Missing element '%s' - check your GStreamer installation."),
2044 "audioconvert"), ("possibly a liboil version mismatch?"));
2045 free_chain ((GstPlayChain *) chain);
2050 post_missing_element_message (playsink, "audioresample");
2051 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2052 (_("Missing element '%s' - check your GStreamer installation."),
2053 "audioresample"), (NULL));
2054 free_chain ((GstPlayChain *) chain);
2059 post_missing_element_message (playsink, "goom");
2060 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2061 (_("Missing element '%s' - check your GStreamer installation."),
2063 free_chain ((GstPlayChain *) chain);
2068 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2069 (NULL), ("Failed to configure the visualisation element."));
2070 /* element made it to READY */
2071 gst_element_set_state (chain->vis, GST_STATE_NULL);
2072 free_chain ((GstPlayChain *) chain);
2077 /* this function is called when all the request pads are requested and when we
2078 * have to construct the final pipeline. Based on the flags we construct the
2079 * final output pipelines.
2082 gst_play_sink_reconfigure (GstPlaySink * playsink)
2085 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2087 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2089 /* assume we need nothing */
2090 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2092 GST_PLAY_SINK_LOCK (playsink);
2093 GST_OBJECT_LOCK (playsink);
2094 /* get flags, there are protected with the object lock */
2095 flags = playsink->flags;
2096 GST_OBJECT_UNLOCK (playsink);
2098 /* figure out which components we need */
2099 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2100 /* we have subtitles and we are requested to show it */
2104 if (((flags & GST_PLAY_FLAG_VIDEO)
2105 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2106 /* we have video and we are requested to show it */
2109 /* we only deinterlace if native video is not requested and
2110 * we have raw video */
2111 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2112 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2113 need_deinterlace = TRUE;
2116 if (playsink->audio_pad) {
2117 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2120 if (playsink->audio_pad_raw) {
2121 /* only can do vis with raw uncompressed audio */
2122 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2123 /* also add video when we add visualisation */
2130 /* we have a text_pad and we need text rendering, in this case we need a
2131 * video_pad to combine the video with the text or visualizations */
2132 if (need_text && !need_video) {
2133 if (playsink->video_pad) {
2135 } else if (need_audio) {
2136 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2137 (_("Can't play a text file without video or visualizations.")),
2138 ("Have text pad but no video pad or visualizations"));
2141 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2142 (_("Can't play a text file without video or visualizations.")),
2143 ("Have text pad but no video pad or visualizations"));
2144 GST_PLAY_SINK_UNLOCK (playsink);
2149 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2150 need_video, need_vis, need_text);
2152 /* set up video pipeline */
2154 gboolean raw, async;
2156 /* we need a raw sink when we do vis or when we have a raw pad */
2157 raw = need_vis ? TRUE : playsink->video_pad_raw;
2158 /* we try to set the sink async=FALSE when we need vis, this way we can
2159 * avoid a queue in the audio chain. */
2162 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2163 playsink->video_pad_raw);
2165 if (playsink->videochain) {
2166 /* try to reactivate the chain */
2167 if (!setup_video_chain (playsink, raw, async)) {
2168 if (playsink->video_sinkpad_stream_synchronizer) {
2169 gst_element_release_request_pad (GST_ELEMENT_CAST
2170 (playsink->stream_synchronizer),
2171 playsink->video_sinkpad_stream_synchronizer);
2172 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2173 playsink->video_sinkpad_stream_synchronizer = NULL;
2174 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2175 playsink->video_srcpad_stream_synchronizer = NULL;
2178 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2180 /* Remove the sink from the bin to keep its state
2181 * and unparent it to allow reuse */
2182 if (playsink->videochain->sink)
2183 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2184 playsink->videochain->sink);
2186 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2187 free_chain ((GstPlayChain *) playsink->videochain);
2188 playsink->videochain = NULL;
2192 if (!playsink->videochain)
2193 playsink->videochain = gen_video_chain (playsink, raw, async);
2194 if (!playsink->videochain)
2197 if (!playsink->video_sinkpad_stream_synchronizer) {
2198 GValue item = { 0, };
2201 playsink->video_sinkpad_stream_synchronizer =
2202 gst_element_get_request_pad (GST_ELEMENT_CAST
2203 (playsink->stream_synchronizer), "sink_%d");
2204 it = gst_pad_iterate_internal_links
2205 (playsink->video_sinkpad_stream_synchronizer);
2207 gst_iterator_next (it, &item);
2208 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2209 g_value_unset (&item);
2210 g_assert (playsink->video_srcpad_stream_synchronizer);
2211 gst_iterator_free (it);
2214 if (playsink->video_pad)
2215 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2216 playsink->video_sinkpad_stream_synchronizer);
2218 if (need_deinterlace) {
2219 if (!playsink->videodeinterlacechain)
2220 playsink->videodeinterlacechain =
2221 gen_video_deinterlace_chain (playsink);
2222 if (!playsink->videodeinterlacechain)
2225 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2227 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2229 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2230 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2232 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2233 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2235 if (playsink->videodeinterlacechain) {
2236 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2237 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2242 GST_DEBUG_OBJECT (playsink, "adding video chain");
2243 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2244 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2245 /* if we are not part of vis or subtitles, set the ghostpad target */
2246 if (!need_vis && !need_text && (!playsink->textchain
2247 || !playsink->text_pad)) {
2248 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2249 if (need_deinterlace)
2250 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2251 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2253 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2254 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2257 GST_DEBUG_OBJECT (playsink, "no video needed");
2258 if (playsink->videochain) {
2259 GST_DEBUG_OBJECT (playsink, "removing video chain");
2260 if (playsink->vischain) {
2263 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2265 /* also had visualisation, release the tee srcpad before we then
2266 * unlink the video from it */
2267 if (playsink->audio_tee_vissrc) {
2268 gst_element_release_request_pad (playsink->audio_tee,
2269 playsink->audio_tee_vissrc);
2270 gst_object_unref (playsink->audio_tee_vissrc);
2271 playsink->audio_tee_vissrc = NULL;
2274 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2275 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2278 if (playsink->video_sinkpad_stream_synchronizer) {
2279 gst_element_release_request_pad (GST_ELEMENT_CAST
2280 (playsink->stream_synchronizer),
2281 playsink->video_sinkpad_stream_synchronizer);
2282 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2283 playsink->video_sinkpad_stream_synchronizer = NULL;
2284 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2285 playsink->video_srcpad_stream_synchronizer = NULL;
2288 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2289 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2290 playsink->videochain->ts_offset = NULL;
2293 if (playsink->videodeinterlacechain) {
2294 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2295 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2298 if (playsink->video_pad)
2299 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2305 GST_DEBUG_OBJECT (playsink, "adding audio");
2307 /* get a raw sink if we are asked for a raw pad */
2308 raw = playsink->audio_pad_raw;
2310 if (playsink->audiochain) {
2311 /* try to reactivate the chain */
2312 if (!setup_audio_chain (playsink, raw)) {
2313 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2314 if (playsink->audio_tee_asrc) {
2315 gst_element_release_request_pad (playsink->audio_tee,
2316 playsink->audio_tee_asrc);
2317 gst_object_unref (playsink->audio_tee_asrc);
2318 playsink->audio_tee_asrc = NULL;
2321 if (playsink->audio_sinkpad_stream_synchronizer) {
2322 gst_element_release_request_pad (GST_ELEMENT_CAST
2323 (playsink->stream_synchronizer),
2324 playsink->audio_sinkpad_stream_synchronizer);
2325 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2326 playsink->audio_sinkpad_stream_synchronizer = NULL;
2327 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2328 playsink->audio_srcpad_stream_synchronizer = NULL;
2331 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2333 /* Remove the sink from the bin to keep its state
2334 * and unparent it to allow reuse */
2335 if (playsink->audiochain->sink)
2336 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2337 playsink->audiochain->sink);
2339 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2340 disconnect_chain (playsink->audiochain, playsink);
2341 playsink->audiochain->volume = NULL;
2342 playsink->audiochain->mute = NULL;
2343 playsink->audiochain->ts_offset = NULL;
2344 free_chain ((GstPlayChain *) playsink->audiochain);
2345 playsink->audiochain = NULL;
2346 playsink->volume_changed = playsink->mute_changed = FALSE;
2350 if (!playsink->audiochain) {
2351 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2352 playsink->audiochain = gen_audio_chain (playsink, raw);
2355 if (!playsink->audio_sinkpad_stream_synchronizer) {
2356 GValue item = { 0, };
2359 playsink->audio_sinkpad_stream_synchronizer =
2360 gst_element_get_request_pad (GST_ELEMENT_CAST
2361 (playsink->stream_synchronizer), "sink_%d");
2362 it = gst_pad_iterate_internal_links
2363 (playsink->audio_sinkpad_stream_synchronizer);
2365 gst_iterator_next (it, &item);
2366 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
2367 g_value_unset (&item);
2368 g_assert (playsink->audio_srcpad_stream_synchronizer);
2369 gst_iterator_free (it);
2372 if (playsink->audiochain) {
2373 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2374 if (playsink->audio_tee_asrc == NULL) {
2375 playsink->audio_tee_asrc =
2376 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2378 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2379 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2380 gst_pad_link_full (playsink->audio_tee_asrc,
2381 playsink->audio_sinkpad_stream_synchronizer,
2382 GST_PAD_LINK_CHECK_NOTHING);
2383 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2384 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2387 GST_DEBUG_OBJECT (playsink, "no audio needed");
2388 /* we have no audio or we are requested to not play audio */
2389 if (playsink->audiochain) {
2390 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2391 /* release the audio pad */
2392 if (playsink->audio_tee_asrc) {
2393 gst_element_release_request_pad (playsink->audio_tee,
2394 playsink->audio_tee_asrc);
2395 gst_object_unref (playsink->audio_tee_asrc);
2396 playsink->audio_tee_asrc = NULL;
2399 if (playsink->audio_sinkpad_stream_synchronizer) {
2400 gst_element_release_request_pad (GST_ELEMENT_CAST
2401 (playsink->stream_synchronizer),
2402 playsink->audio_sinkpad_stream_synchronizer);
2403 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2404 playsink->audio_sinkpad_stream_synchronizer = NULL;
2405 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2406 playsink->audio_srcpad_stream_synchronizer = NULL;
2409 if (playsink->audiochain->sink_volume) {
2410 disconnect_chain (playsink->audiochain, playsink);
2411 playsink->audiochain->volume = NULL;
2412 playsink->audiochain->mute = NULL;
2413 playsink->audiochain->ts_offset = NULL;
2415 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2416 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2423 if (!playsink->vischain)
2424 playsink->vischain = gen_vis_chain (playsink);
2426 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2428 if (playsink->vischain) {
2429 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2431 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2432 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2433 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2434 if (playsink->audio_tee_vissrc == NULL) {
2435 playsink->audio_tee_vissrc =
2436 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2438 gst_pad_link_full (playsink->audio_tee_vissrc,
2439 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2440 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2441 GST_PAD_LINK_CHECK_NOTHING);
2442 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2443 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2444 gst_object_unref (srcpad);
2447 GST_DEBUG_OBJECT (playsink, "no vis needed");
2448 if (playsink->vischain) {
2449 if (playsink->audio_tee_vissrc) {
2450 gst_element_release_request_pad (playsink->audio_tee,
2451 playsink->audio_tee_vissrc);
2452 gst_object_unref (playsink->audio_tee_vissrc);
2453 playsink->audio_tee_vissrc = NULL;
2455 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2456 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2457 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2462 GST_DEBUG_OBJECT (playsink, "adding text");
2463 if (!playsink->textchain) {
2464 GST_DEBUG_OBJECT (playsink, "creating text chain");
2465 playsink->textchain = gen_text_chain (playsink);
2467 if (playsink->textchain) {
2470 GST_DEBUG_OBJECT (playsink, "adding text chain");
2471 if (playsink->textchain->overlay)
2472 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2474 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2476 if (!playsink->text_sinkpad_stream_synchronizer) {
2477 GValue item = { 0, };
2479 playsink->text_sinkpad_stream_synchronizer =
2480 gst_element_get_request_pad (GST_ELEMENT_CAST
2481 (playsink->stream_synchronizer), "sink_%d");
2482 it = gst_pad_iterate_internal_links
2483 (playsink->text_sinkpad_stream_synchronizer);
2485 gst_iterator_next (it, &item);
2486 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
2487 g_value_unset (&item);
2488 g_assert (playsink->text_srcpad_stream_synchronizer);
2489 gst_iterator_free (it);
2491 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2492 playsink->text_sinkpad_stream_synchronizer);
2493 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2494 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2501 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2502 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2503 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2504 GST_PAD_LINK_CHECK_NOTHING);
2505 gst_object_unref (srcpad);
2507 if (need_deinterlace)
2508 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2509 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2511 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2512 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2514 gst_pad_link_full (playsink->textchain->srcpad,
2515 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2517 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2520 GST_DEBUG_OBJECT (playsink, "no text needed");
2521 /* we have no subtitles/text or we are requested to not show them */
2523 if (playsink->text_sinkpad_stream_synchronizer) {
2524 gst_element_release_request_pad (GST_ELEMENT_CAST
2525 (playsink->stream_synchronizer),
2526 playsink->text_sinkpad_stream_synchronizer);
2527 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2528 playsink->text_sinkpad_stream_synchronizer = NULL;
2529 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2530 playsink->text_srcpad_stream_synchronizer = NULL;
2533 if (playsink->textchain) {
2534 if (playsink->text_pad == NULL) {
2535 /* no text pad, remove the chain entirely */
2536 GST_DEBUG_OBJECT (playsink, "removing text chain");
2537 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2538 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2540 /* we have a chain and a textpad, turn the subtitles off */
2541 GST_DEBUG_OBJECT (playsink, "turning off the text");
2542 if (playsink->textchain->overlay)
2543 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2547 if (!need_video && playsink->video_pad) {
2548 if (playsink->video_sinkpad_stream_synchronizer) {
2549 gst_element_release_request_pad (GST_ELEMENT_CAST
2550 (playsink->stream_synchronizer),
2551 playsink->video_sinkpad_stream_synchronizer);
2552 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2553 playsink->video_sinkpad_stream_synchronizer = NULL;
2554 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2555 playsink->video_srcpad_stream_synchronizer = NULL;
2558 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2561 if (playsink->text_pad && !playsink->textchain)
2562 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2564 update_av_offset (playsink);
2565 do_async_done (playsink);
2566 GST_PLAY_SINK_UNLOCK (playsink);
2573 /* gen_ chain already posted error */
2574 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2575 GST_PLAY_SINK_UNLOCK (playsink);
2581 * gst_play_sink_set_flags:
2582 * @playsink: a #GstPlaySink
2583 * @flags: #GstPlayFlags
2585 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2586 * when constructing the sink pipelins.
2588 * Returns: TRUE if the flags could be configured.
2591 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2593 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2595 GST_OBJECT_LOCK (playsink);
2596 playsink->flags = flags;
2597 GST_OBJECT_UNLOCK (playsink);
2603 * gst_play_sink_get_flags:
2604 * @playsink: a #GstPlaySink
2606 * Get the flags of @playsink. That flags control the behaviour of the sink when
2607 * it constructs the sink pipelines.
2609 * Returns: the currently configured #GstPlayFlags.
2612 gst_play_sink_get_flags (GstPlaySink * playsink)
2616 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2618 GST_OBJECT_LOCK (playsink);
2619 res = playsink->flags;
2620 GST_OBJECT_UNLOCK (playsink);
2626 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2628 GstPlayTextChain *chain;
2630 GST_PLAY_SINK_LOCK (playsink);
2631 chain = (GstPlayTextChain *) playsink->textchain;
2632 g_free (playsink->font_desc);
2633 playsink->font_desc = g_strdup (desc);
2634 if (chain && chain->overlay) {
2635 g_object_set (chain->overlay, "font-desc", desc, NULL);
2637 GST_PLAY_SINK_UNLOCK (playsink);
2641 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2643 gchar *result = NULL;
2644 GstPlayTextChain *chain;
2646 GST_PLAY_SINK_LOCK (playsink);
2647 chain = (GstPlayTextChain *) playsink->textchain;
2648 if (chain && chain->overlay) {
2649 g_object_get (chain->overlay, "font-desc", &result, NULL);
2650 playsink->font_desc = g_strdup (result);
2652 result = g_strdup (playsink->font_desc);
2654 GST_PLAY_SINK_UNLOCK (playsink);
2660 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2661 const gchar * encoding)
2663 GstPlayTextChain *chain;
2665 GST_PLAY_SINK_LOCK (playsink);
2666 chain = (GstPlayTextChain *) playsink->textchain;
2667 g_free (playsink->subtitle_encoding);
2668 playsink->subtitle_encoding = g_strdup (encoding);
2669 if (chain && chain->overlay) {
2670 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2672 GST_PLAY_SINK_UNLOCK (playsink);
2676 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2678 gchar *result = NULL;
2679 GstPlayTextChain *chain;
2681 GST_PLAY_SINK_LOCK (playsink);
2682 chain = (GstPlayTextChain *) playsink->textchain;
2683 if (chain && chain->overlay) {
2684 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2685 playsink->subtitle_encoding = g_strdup (result);
2687 result = g_strdup (playsink->subtitle_encoding);
2689 GST_PLAY_SINK_UNLOCK (playsink);
2695 update_av_offset (GstPlaySink * playsink)
2698 GstPlayAudioChain *achain;
2699 GstPlayVideoChain *vchain;
2701 av_offset = playsink->av_offset;
2702 achain = (GstPlayAudioChain *) playsink->audiochain;
2703 vchain = (GstPlayVideoChain *) playsink->videochain;
2705 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2706 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2707 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2709 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2714 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2716 GST_PLAY_SINK_LOCK (playsink);
2717 playsink->av_offset = av_offset;
2718 update_av_offset (playsink);
2719 GST_PLAY_SINK_UNLOCK (playsink);
2723 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2727 GST_PLAY_SINK_LOCK (playsink);
2728 result = playsink->av_offset;
2729 GST_PLAY_SINK_UNLOCK (playsink);
2735 * gst_play_sink_get_last_frame:
2736 * @playsink: a #GstPlaySink
2738 * Get the last displayed frame from @playsink. This frame is in the native
2739 * format of the sink element, the caps on the result buffer contain the format
2740 * of the frame data.
2742 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2746 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2748 GstBuffer *result = NULL;
2749 GstPlayVideoChain *chain;
2751 GST_PLAY_SINK_LOCK (playsink);
2752 GST_DEBUG_OBJECT (playsink, "taking last frame");
2753 /* get the video chain if we can */
2754 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2755 GST_DEBUG_OBJECT (playsink, "found video chain");
2756 /* see if the chain is active */
2757 if (chain->chain.activated && chain->sink) {
2760 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2762 /* find and get the last-buffer property now */
2764 gst_play_sink_find_property (playsink, chain->sink,
2765 "last-buffer", GST_TYPE_BUFFER))) {
2766 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2767 g_object_get (elem, "last-buffer", &result, NULL);
2768 gst_object_unref (elem);
2772 GST_PLAY_SINK_UNLOCK (playsink);
2778 * gst_play_sink_convert_frame:
2779 * @playsink: a #GstPlaySink
2782 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2783 * be in the native format of the sink element and the caps on the buffer
2784 * describe the format of the frame. If @caps is not %NULL, the video
2785 * frame will be converted to the format of the caps.
2787 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2788 * available or when the conversion failed.
2791 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2795 result = gst_play_sink_get_last_frame (playsink);
2796 if (result != NULL && caps != NULL) {
2800 /* FIXME, need to get the input buffer caps */
2801 temp = gst_video_convert_frame (result, NULL, caps, 25 * GST_SECOND, &err);
2802 gst_buffer_unref (result);
2803 if (temp == NULL && err) {
2804 /* I'm really uncertain whether we should make playsink post an error
2805 * on the bus or not. It's not like it's a critical issue regarding
2806 * playsink behaviour. */
2807 GST_ERROR ("Error converting frame: %s", err->message);
2815 is_raw_structure (GstStructure * s)
2819 name = gst_structure_get_name (s);
2821 if (g_str_has_prefix (name, "video/x-raw-") ||
2822 g_str_has_prefix (name, "audio/x-raw-"))
2828 is_raw_pad (GstPad * pad)
2830 GstPad *peer = gst_pad_get_peer (pad);
2832 gboolean raw = TRUE;
2837 caps = gst_pad_get_negotiated_caps (peer);
2841 caps = gst_pad_get_caps (peer, NULL);
2843 n = gst_caps_get_size (caps);
2844 for (i = 0; i < n; i++) {
2845 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2849 } else if (raw != r) {
2850 GST_ERROR_OBJECT (pad,
2851 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2857 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2859 gst_caps_unref (caps);
2860 gst_object_unref (peer);
2865 static GstProbeReturn
2866 sinkpad_blocked_cb (GstPad * blockedpad, GstProbeType type, gpointer type_data,
2867 gpointer user_data);
2870 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
2872 if (playsink->video_pad) {
2874 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2875 (playsink->video_pad)));
2876 if (blocked && playsink->video_block_id == 0) {
2877 playsink->video_block_id =
2878 gst_pad_add_probe (opad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
2879 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2880 } else if (!blocked && playsink->video_block_id) {
2881 gst_pad_remove_probe (opad, playsink->video_block_id);
2882 playsink->video_block_id = 0;
2883 playsink->video_pad_blocked = FALSE;
2885 gst_object_unref (opad);
2890 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
2892 if (playsink->audio_pad) {
2894 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2895 (playsink->audio_pad)));
2896 if (blocked && playsink->audio_block_id == 0) {
2897 playsink->audio_block_id =
2898 gst_pad_add_probe (opad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
2899 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2900 } else if (!blocked && playsink->audio_block_id) {
2901 gst_pad_remove_probe (opad, playsink->audio_block_id);
2902 playsink->audio_block_id = 0;
2903 playsink->audio_pad_blocked = FALSE;
2905 gst_object_unref (opad);
2910 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
2912 if (playsink->text_pad) {
2914 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2915 (playsink->text_pad)));
2916 if (blocked && playsink->text_block_id == 0) {
2917 playsink->text_block_id =
2918 gst_pad_add_probe (opad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
2919 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2920 } else if (!blocked && playsink->text_block_id) {
2921 gst_pad_remove_probe (opad, playsink->text_block_id);
2922 playsink->text_block_id = 0;
2923 playsink->text_pad_blocked = FALSE;
2925 gst_object_unref (opad);
2929 static GstProbeReturn
2930 sinkpad_blocked_cb (GstPad * blockedpad, GstProbeType type, gpointer type_data,
2933 GstPlaySink *playsink = (GstPlaySink *) user_data;
2936 GST_PLAY_SINK_LOCK (playsink);
2938 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2939 if (pad == playsink->video_pad) {
2940 playsink->video_pad_blocked = TRUE;
2941 GST_DEBUG_OBJECT (pad, "Video pad blocked");
2942 } else if (pad == playsink->audio_pad) {
2943 playsink->audio_pad_blocked = TRUE;
2944 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
2945 } else if (pad == playsink->text_pad) {
2946 playsink->text_pad_blocked = TRUE;
2947 GST_DEBUG_OBJECT (pad, "Text pad blocked");
2950 if ((!playsink->video_pad || playsink->video_pad_blocked) &&
2951 (!playsink->audio_pad || playsink->audio_pad_blocked) &&
2952 (!playsink->text_pad || playsink->text_pad_blocked)) {
2953 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
2955 if (playsink->video_pad) {
2956 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
2957 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
2958 playsink->video_pad_raw);
2961 if (playsink->audio_pad) {
2962 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
2963 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
2964 playsink->audio_pad_raw);
2967 gst_play_sink_reconfigure (playsink);
2969 video_set_blocked (playsink, FALSE);
2970 audio_set_blocked (playsink, FALSE);
2971 text_set_blocked (playsink, FALSE);
2974 gst_object_unref (pad);
2976 GST_PLAY_SINK_UNLOCK (playsink);
2978 return GST_PROBE_OK;
2982 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
2984 gboolean reconfigure = FALSE;
2988 g_object_get (pad, "caps", &caps, NULL);
2992 if (pad == playsink->audio_pad) {
2993 raw = is_raw_pad (pad);
2994 reconfigure = (!!playsink->audio_pad_raw != !!raw)
2995 && playsink->audiochain;
2996 GST_DEBUG_OBJECT (pad,
2997 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
2999 } else if (pad == playsink->video_pad) {
3000 raw = is_raw_pad (pad);
3001 reconfigure = (!!playsink->video_pad_raw != !!raw)
3002 && playsink->videochain;
3003 GST_DEBUG_OBJECT (pad,
3004 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3008 gst_caps_unref (caps);
3011 GST_PLAY_SINK_LOCK (playsink);
3012 video_set_blocked (playsink, TRUE);
3013 audio_set_blocked (playsink, TRUE);
3014 text_set_blocked (playsink, TRUE);
3015 GST_PLAY_SINK_UNLOCK (playsink);
3020 * gst_play_sink_request_pad
3021 * @playsink: a #GstPlaySink
3022 * @type: a #GstPlaySinkType
3024 * Create or return a pad of @type.
3026 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3029 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3032 gboolean created = FALSE;
3033 gboolean activate = TRUE;
3034 const gchar *pad_name = NULL;
3035 gulong *block_id = NULL;
3037 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3039 GST_PLAY_SINK_LOCK (playsink);
3041 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3042 case GST_PLAY_SINK_TYPE_AUDIO:
3043 pad_name = "audio_sink";
3044 if (!playsink->audio_tee) {
3045 GST_LOG_OBJECT (playsink, "creating tee");
3046 /* create tee when needed. This element will feed the audio sink chain
3047 * and the vis chain. */
3048 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3049 if (playsink->audio_tee == NULL) {
3050 post_missing_element_message (playsink, "tee");
3051 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3052 (_("Missing element '%s' - check your GStreamer installation."),
3057 playsink->audio_tee_sink =
3058 gst_element_get_static_pad (playsink->audio_tee, "sink");
3059 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3060 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3063 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3065 if (!playsink->audio_pad) {
3066 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3067 playsink->audio_pad =
3068 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3069 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3070 G_CALLBACK (caps_notify_cb), playsink);
3073 playsink->audio_pad_raw = FALSE;
3074 res = playsink->audio_pad;
3075 block_id = &playsink->audio_block_id;
3077 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3078 case GST_PLAY_SINK_TYPE_VIDEO:
3079 pad_name = "video_sink";
3080 if (!playsink->video_pad) {
3081 GST_LOG_OBJECT (playsink, "ghosting videosink");
3082 playsink->video_pad =
3083 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3084 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3085 G_CALLBACK (caps_notify_cb), playsink);
3088 playsink->video_pad_raw = FALSE;
3089 res = playsink->video_pad;
3090 block_id = &playsink->video_block_id;
3092 case GST_PLAY_SINK_TYPE_TEXT:
3093 GST_LOG_OBJECT (playsink, "ghosting text");
3094 if (!playsink->text_pad) {
3095 playsink->text_pad =
3096 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3099 res = playsink->text_pad;
3100 block_id = &playsink->text_block_id;
3102 case GST_PLAY_SINK_TYPE_FLUSHING:
3106 /* we need a unique padname for the flushing pad. */
3107 padname = g_strdup_printf ("flushing_%d", playsink->count);
3108 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3119 GST_PLAY_SINK_UNLOCK (playsink);
3121 if (created && res) {
3122 /* we have to add the pad when it's active or we get an error when the
3123 * element is 'running' */
3124 gst_pad_set_active (res, TRUE);
3125 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3126 if (block_id && *block_id == 0) {
3128 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3131 gst_pad_add_probe (blockpad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
3132 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3133 gst_object_unref (blockpad);
3136 gst_pad_set_active (res, activate);
3143 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3144 const gchar * name, const GstCaps * caps)
3148 GstPlaySinkType type;
3149 const gchar *tplname;
3151 g_return_val_if_fail (templ != NULL, NULL);
3153 GST_DEBUG_OBJECT (element, "name:%s", name);
3155 psink = GST_PLAY_SINK (element);
3156 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3158 /* Figure out the GstPlaySinkType based on the template */
3159 if (!strcmp (tplname, "audio_sink"))
3160 type = GST_PLAY_SINK_TYPE_AUDIO;
3161 else if (!strcmp (tplname, "audio_raw_sink"))
3162 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3163 else if (!strcmp (tplname, "video_sink"))
3164 type = GST_PLAY_SINK_TYPE_VIDEO;
3165 else if (!strcmp (tplname, "video_raw_sink"))
3166 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3167 else if (!strcmp (tplname, "text_sink"))
3168 type = GST_PLAY_SINK_TYPE_TEXT;
3170 goto unknown_template;
3172 pad = gst_play_sink_request_pad (psink, type);
3176 GST_WARNING_OBJECT (element, "Unknown pad template");
3181 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3183 GstPad **res = NULL;
3184 gboolean untarget = TRUE;
3186 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3188 GST_PLAY_SINK_LOCK (playsink);
3189 if (pad == playsink->video_pad) {
3190 res = &playsink->video_pad;
3191 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3193 } else if (pad == playsink->audio_pad) {
3194 res = &playsink->audio_pad;
3195 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3197 } else if (pad == playsink->text_pad) {
3198 res = &playsink->text_pad;
3200 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3204 GST_PLAY_SINK_UNLOCK (playsink);
3207 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3208 gst_pad_set_active (*res, FALSE);
3210 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3211 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3213 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3214 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3220 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3222 GstPlaySink *psink = GST_PLAY_SINK (element);
3224 gst_play_sink_release_pad (psink, pad);
3228 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3230 GstPlaySink *playsink;
3232 playsink = GST_PLAY_SINK_CAST (bin);
3234 switch (GST_MESSAGE_TYPE (message)) {
3235 case GST_MESSAGE_STEP_DONE:
3240 gboolean flush, intermediate, eos;
3243 GST_INFO_OBJECT (playsink, "Handling step-done message");
3244 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3245 &intermediate, &duration, &eos);
3247 if (format == GST_FORMAT_BUFFERS) {
3248 /* for the buffer format, we align the other streams */
3249 if (playsink->audiochain) {
3253 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3256 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3257 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3261 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3265 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3270 /* Send an event to our sinks until one of them works; don't then send to the
3271 * remaining sinks (unlike GstBin)
3272 * Special case: If a text sink is set we need to send the event
3273 * to them in case it's source is different from the a/v stream's source.
3276 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3278 gboolean res = TRUE;
3280 if (playsink->textchain && playsink->textchain->sink) {
3281 gst_event_ref (event);
3282 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3283 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to text sink");
3285 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3289 if (playsink->videochain) {
3290 gst_event_ref (event);
3291 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3292 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
3295 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3297 if (playsink->audiochain) {
3298 gst_event_ref (event);
3299 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3300 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
3303 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3307 gst_event_unref (event);
3311 /* We only want to send the event to a single sink (overriding GstBin's
3312 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3313 * events appropriately. So, this is a messy duplication of code. */
3315 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3317 gboolean res = FALSE;
3318 GstEventType event_type = GST_EVENT_TYPE (event);
3319 GstPlaySink *playsink;
3321 playsink = GST_PLAY_SINK_CAST (element);
3323 switch (event_type) {
3324 case GST_EVENT_SEEK:
3325 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3326 res = gst_play_sink_send_event_to_sink (playsink, event);
3328 case GST_EVENT_STEP:
3333 gboolean flush, intermediate;
3335 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3338 if (format == GST_FORMAT_BUFFERS) {
3339 /* for buffers, we will try to step video frames, for other formats we
3340 * send the step to all sinks */
3341 res = gst_play_sink_send_event_to_sink (playsink, event);
3344 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3351 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3358 static GstStateChangeReturn
3359 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3361 GstStateChangeReturn ret;
3362 GstStateChangeReturn bret;
3364 GstPlaySink *playsink;
3366 playsink = GST_PLAY_SINK (element);
3368 switch (transition) {
3369 case GST_STATE_CHANGE_READY_TO_PAUSED:
3370 playsink->need_async_start = TRUE;
3371 /* we want to go async to PAUSED until we managed to configure and add the
3373 do_async_start (playsink);
3374 ret = GST_STATE_CHANGE_ASYNC;
3376 case GST_STATE_CHANGE_PAUSED_TO_READY:
3377 /* unblock all pads here */
3378 GST_PLAY_SINK_LOCK (playsink);
3379 video_set_blocked (playsink, FALSE);
3380 audio_set_blocked (playsink, FALSE);
3381 text_set_blocked (playsink, FALSE);
3382 GST_PLAY_SINK_UNLOCK (playsink);
3384 case GST_STATE_CHANGE_READY_TO_NULL:
3385 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3386 /* remove our links to the mute and volume elements when they were
3387 * provided by a sink */
3388 disconnect_chain (playsink->audiochain, playsink);
3389 playsink->audiochain->volume = NULL;
3390 playsink->audiochain->mute = NULL;
3391 playsink->audiochain->ts_offset = NULL;
3393 ret = GST_STATE_CHANGE_SUCCESS;
3396 /* all other state changes return SUCCESS by default, this value can be
3397 * overridden by the result of the children */
3398 ret = GST_STATE_CHANGE_SUCCESS;
3402 /* do the state change of the children */
3404 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3406 /* now look at the result of our children and adjust the return value */
3408 case GST_STATE_CHANGE_FAILURE:
3409 /* failure, we stop */
3410 goto activate_failed;
3411 case GST_STATE_CHANGE_NO_PREROLL:
3412 /* some child returned NO_PREROLL. This is strange but we never know. We
3413 * commit our async state change (if any) and return the NO_PREROLL */
3414 do_async_done (playsink);
3417 case GST_STATE_CHANGE_ASYNC:
3418 /* some child was async, return this */
3422 /* return our previously configured return value */
3426 switch (transition) {
3427 case GST_STATE_CHANGE_READY_TO_PAUSED:
3429 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3430 /* FIXME Release audio device when we implement that */
3431 playsink->need_async_start = TRUE;
3433 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3434 if (playsink->video_sinkpad_stream_synchronizer) {
3435 gst_element_release_request_pad (GST_ELEMENT_CAST
3436 (playsink->stream_synchronizer),
3437 playsink->video_sinkpad_stream_synchronizer);
3438 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3439 playsink->video_sinkpad_stream_synchronizer = NULL;
3440 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3441 playsink->video_srcpad_stream_synchronizer = NULL;
3443 if (playsink->audio_sinkpad_stream_synchronizer) {
3444 gst_element_release_request_pad (GST_ELEMENT_CAST
3445 (playsink->stream_synchronizer),
3446 playsink->audio_sinkpad_stream_synchronizer);
3447 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3448 playsink->audio_sinkpad_stream_synchronizer = NULL;
3449 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3450 playsink->audio_srcpad_stream_synchronizer = NULL;
3452 if (playsink->text_sinkpad_stream_synchronizer) {
3453 gst_element_release_request_pad (GST_ELEMENT_CAST
3454 (playsink->stream_synchronizer),
3455 playsink->text_sinkpad_stream_synchronizer);
3456 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3457 playsink->text_sinkpad_stream_synchronizer = NULL;
3458 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3459 playsink->text_srcpad_stream_synchronizer = NULL;
3463 case GST_STATE_CHANGE_READY_TO_NULL:
3464 /* remove sinks we added */
3465 if (playsink->videodeinterlacechain) {
3466 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3468 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3470 if (playsink->videochain) {
3471 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3472 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3474 if (playsink->audiochain) {
3475 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3476 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3478 if (playsink->vischain) {
3479 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3480 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3482 if (playsink->textchain) {
3483 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3484 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3486 do_async_done (playsink);
3487 /* when going to READY, keep elements around as long as possible,
3488 * so they may be re-used faster next time/url around.
3489 * when really going to NULL, clean up everything completely. */
3490 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3492 /* Unparent the sinks to allow reuse */
3493 if (playsink->videochain && playsink->videochain->sink)
3494 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3495 playsink->videochain->sink);
3496 if (playsink->audiochain && playsink->audiochain->sink)
3497 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3498 playsink->audiochain->sink);
3499 if (playsink->textchain && playsink->textchain->sink)
3500 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3501 playsink->textchain->sink);
3503 if (playsink->audio_sink != NULL)
3504 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3505 if (playsink->video_sink != NULL)
3506 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3507 if (playsink->visualisation != NULL)
3508 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3509 if (playsink->text_sink != NULL)
3510 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3512 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3513 playsink->videodeinterlacechain = NULL;
3514 free_chain ((GstPlayChain *) playsink->videochain);
3515 playsink->videochain = NULL;
3516 free_chain ((GstPlayChain *) playsink->audiochain);
3517 playsink->audiochain = NULL;
3518 free_chain ((GstPlayChain *) playsink->vischain);
3519 playsink->vischain = NULL;
3520 free_chain ((GstPlayChain *) playsink->textchain);
3521 playsink->textchain = NULL;
3532 GST_DEBUG_OBJECT (element,
3533 "element failed to change states -- activation problem?");
3534 return GST_STATE_CHANGE_FAILURE;
3539 gst_play_sink_set_property (GObject * object, guint prop_id,
3540 const GValue * value, GParamSpec * spec)
3542 GstPlaySink *playsink = GST_PLAY_SINK (object);
3546 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3549 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3552 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3554 case PROP_FONT_DESC:
3555 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3557 case PROP_SUBTITLE_ENCODING:
3558 gst_play_sink_set_subtitle_encoding (playsink,
3559 g_value_get_string (value));
3561 case PROP_VIS_PLUGIN:
3562 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3564 case PROP_AV_OFFSET:
3565 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3568 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3574 gst_play_sink_get_property (GObject * object, guint prop_id,
3575 GValue * value, GParamSpec * spec)
3577 GstPlaySink *playsink = GST_PLAY_SINK (object);
3581 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3584 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3587 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3589 case PROP_FONT_DESC:
3590 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3592 case PROP_SUBTITLE_ENCODING:
3593 g_value_take_string (value,
3594 gst_play_sink_get_subtitle_encoding (playsink));
3596 case PROP_VIS_PLUGIN:
3597 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3600 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3602 case PROP_AV_OFFSET:
3603 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3606 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3613 gst_play_sink_plugin_init (GstPlugin * plugin)
3615 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3617 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3618 GST_TYPE_PLAY_SINK);