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;
157 GstElement *audio_tee;
158 GstPad *audio_tee_sink;
159 GstPad *audio_tee_asrc;
160 GstPad *audio_tee_vissrc;
163 gboolean video_pad_raw;
164 gboolean video_pad_blocked;
165 GstPad *video_srcpad_stream_synchronizer;
166 GstPad *video_sinkpad_stream_synchronizer;
169 gboolean text_pad_blocked;
170 GstPad *text_srcpad_stream_synchronizer;
171 GstPad *text_sinkpad_stream_synchronizer;
174 GstElement *audio_sink;
175 GstElement *video_sink;
176 GstElement *visualisation;
177 GstElement *text_sink;
180 gchar *font_desc; /* font description */
181 gchar *subtitle_encoding; /* subtitle encoding */
182 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
184 gboolean volume_changed; /* volume/mute changed while no audiochain */
185 gboolean mute_changed; /* ... has been created yet */
189 struct _GstPlaySinkClass
191 GstBinClass parent_class;
193 gboolean (*reconfigure) (GstPlaySink * playsink);
195 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
199 static GstStaticPadTemplate audiotemplate =
200 GST_STATIC_PAD_TEMPLATE ("audio_sink",
203 GST_STATIC_CAPS_ANY);
204 static GstStaticPadTemplate videotemplate =
205 GST_STATIC_PAD_TEMPLATE ("video_sink",
208 GST_STATIC_CAPS_ANY);
209 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
212 GST_STATIC_CAPS_ANY);
214 /* FIXME 0.11: Remove */
215 static GstStaticPadTemplate audiorawtemplate =
216 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
219 GST_STATIC_CAPS_ANY);
220 static GstStaticPadTemplate videorawtemplate =
221 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
224 GST_STATIC_CAPS_ANY);
235 PROP_SUBTITLE_ENCODING,
248 static void gst_play_sink_dispose (GObject * object);
249 static void gst_play_sink_finalize (GObject * object);
250 static void gst_play_sink_set_property (GObject * object, guint prop_id,
251 const GValue * value, GParamSpec * spec);
252 static void gst_play_sink_get_property (GObject * object, guint prop_id,
253 GValue * value, GParamSpec * spec);
255 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
256 GstPadTemplate * templ, const gchar * name);
257 static void gst_play_sink_release_request_pad (GstElement * element,
259 static gboolean gst_play_sink_send_event (GstElement * element,
261 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
262 GstStateChange transition);
264 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
266 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
267 GstPlaySink * playsink);
268 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
269 GstPlaySink * playsink);
271 static void update_av_offset (GstPlaySink * playsink);
274 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
275 GValue * return_value G_GNUC_UNUSED,
276 guint n_param_values,
277 const GValue * param_values,
278 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
280 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
281 gpointer arg_1, gpointer data2);
282 register GMarshalFunc_OBJECT__BOXED callback;
283 register GCClosure *cc = (GCClosure *) closure;
284 register gpointer data1, data2;
286 g_return_if_fail (return_value != NULL);
287 g_return_if_fail (n_param_values == 2);
289 if (G_CCLOSURE_SWAP_DATA (closure)) {
290 data1 = closure->data;
291 data2 = g_value_peek_pointer (param_values + 0);
293 data1 = g_value_peek_pointer (param_values + 0);
294 data2 = closure->data;
297 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
299 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
301 gst_value_take_buffer (return_value, v_return);
304 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
306 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
309 gst_play_sink_class_init (GstPlaySinkClass * klass)
311 GObjectClass *gobject_klass;
312 GstElementClass *gstelement_klass;
313 GstBinClass *gstbin_klass;
315 gobject_klass = (GObjectClass *) klass;
316 gstelement_klass = (GstElementClass *) klass;
317 gstbin_klass = (GstBinClass *) klass;
319 gobject_klass->dispose = gst_play_sink_dispose;
320 gobject_klass->finalize = gst_play_sink_finalize;
321 gobject_klass->set_property = gst_play_sink_set_property;
322 gobject_klass->get_property = gst_play_sink_get_property;
328 * Control the behaviour of playsink.
330 g_object_class_install_property (gobject_klass, PROP_FLAGS,
331 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
332 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
333 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
336 * GstPlaySink:volume:
338 * Get or set the current audio stream volume. 1.0 means 100%,
339 * 0.0 means mute. This uses a linear volume scale.
342 g_object_class_install_property (gobject_klass, PROP_VOLUME,
343 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
344 0.0, VOLUME_MAX_DOUBLE, 1.0,
345 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
346 g_object_class_install_property (gobject_klass, PROP_MUTE,
347 g_param_spec_boolean ("mute", "Mute",
348 "Mute the audio channel without changing the volume", FALSE,
349 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
350 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
351 g_param_spec_string ("subtitle-font-desc",
352 "Subtitle font description",
353 "Pango font description of font "
354 "to be used for subtitle rendering", NULL,
355 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
356 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
357 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
358 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
359 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
360 "be checked for an encoding to use. If that is not set either, "
361 "ISO-8859-15 will be assumed.", NULL,
362 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
363 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
364 g_param_spec_object ("vis-plugin", "Vis plugin",
365 "the visualization element to use (NULL = default)",
366 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
370 * Get the currently rendered or prerolled frame in the video sink.
371 * The #GstCaps on the buffer will describe the format of the buffer.
375 g_object_class_install_property (gobject_klass, PROP_FRAME,
376 gst_param_spec_mini_object ("frame", "Frame",
377 "The last frame (NULL = no video available)",
378 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
380 * GstPlaySink:av-offset:
382 * Control the synchronisation offset between the audio and video streams.
383 * Positive values make the audio ahead of the video and negative values make
384 * the audio go behind the video.
388 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
389 g_param_spec_int64 ("av-offset", "AV Offset",
390 "The synchronisation offset between audio and video in nanoseconds",
391 G_MININT64, G_MAXINT64, 0,
392 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
394 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
395 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
396 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
399 * GstPlaySink::convert-frame
400 * @playsink: a #GstPlaySink
401 * @caps: the target format of the frame
403 * Action signal to retrieve the currently playing video frame in the format
404 * specified by @caps.
405 * If @caps is %NULL, no conversion will be performed and this function is
406 * equivalent to the #GstPlaySink::frame property.
408 * Returns: a #GstBuffer of the current video frame converted to #caps.
409 * The caps on the buffer will describe the final layout of the buffer data.
410 * %NULL is returned when no current buffer can be retrieved or when the
415 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
416 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
417 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
418 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
420 gst_element_class_add_pad_template (gstelement_klass,
421 gst_static_pad_template_get (&audiorawtemplate));
422 gst_element_class_add_pad_template (gstelement_klass,
423 gst_static_pad_template_get (&audiotemplate));
424 gst_element_class_add_pad_template (gstelement_klass,
425 gst_static_pad_template_get (&videorawtemplate));
426 gst_element_class_add_pad_template (gstelement_klass,
427 gst_static_pad_template_get (&videotemplate));
428 gst_element_class_add_pad_template (gstelement_klass,
429 gst_static_pad_template_get (&texttemplate));
430 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
432 "Convenience sink for multiple streams",
433 "Wim Taymans <wim.taymans@gmail.com>");
435 gstelement_klass->change_state =
436 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
437 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
438 gstelement_klass->request_new_pad =
439 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
440 gstelement_klass->release_pad =
441 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
443 gstbin_klass->handle_message =
444 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
446 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
447 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
451 gst_play_sink_init (GstPlaySink * playsink)
454 playsink->video_sink = NULL;
455 playsink->audio_sink = NULL;
456 playsink->visualisation = NULL;
457 playsink->text_sink = NULL;
458 playsink->volume = 1.0;
459 playsink->font_desc = NULL;
460 playsink->subtitle_encoding = NULL;
461 playsink->flags = DEFAULT_FLAGS;
463 playsink->stream_synchronizer =
464 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
465 gst_bin_add (GST_BIN_CAST (playsink),
466 GST_ELEMENT_CAST (playsink->stream_synchronizer));
468 g_static_rec_mutex_init (&playsink->lock);
469 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
473 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
477 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
480 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
486 free_chain (GstPlayChain * chain)
490 gst_object_unref (chain->bin);
496 gst_play_sink_dispose (GObject * object)
498 GstPlaySink *playsink;
500 playsink = GST_PLAY_SINK (object);
502 if (playsink->audio_sink != NULL) {
503 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
504 gst_object_unref (playsink->audio_sink);
505 playsink->audio_sink = NULL;
507 if (playsink->video_sink != NULL) {
508 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
509 gst_object_unref (playsink->video_sink);
510 playsink->video_sink = NULL;
512 if (playsink->visualisation != NULL) {
513 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
514 gst_object_unref (playsink->visualisation);
515 playsink->visualisation = NULL;
517 if (playsink->text_sink != NULL) {
518 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
519 gst_object_unref (playsink->text_sink);
520 playsink->text_sink = NULL;
523 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
524 playsink->videodeinterlacechain = NULL;
525 free_chain ((GstPlayChain *) playsink->videochain);
526 playsink->videochain = NULL;
527 free_chain ((GstPlayChain *) playsink->audiochain);
528 playsink->audiochain = NULL;
529 free_chain ((GstPlayChain *) playsink->vischain);
530 playsink->vischain = NULL;
531 free_chain ((GstPlayChain *) playsink->textchain);
532 playsink->textchain = NULL;
534 if (playsink->audio_tee_sink) {
535 gst_object_unref (playsink->audio_tee_sink);
536 playsink->audio_tee_sink = NULL;
539 if (playsink->audio_tee_vissrc) {
540 gst_element_release_request_pad (playsink->audio_tee,
541 playsink->audio_tee_vissrc);
542 gst_object_unref (playsink->audio_tee_vissrc);
543 playsink->audio_tee_vissrc = NULL;
546 if (playsink->audio_tee_asrc) {
547 gst_element_release_request_pad (playsink->audio_tee,
548 playsink->audio_tee_asrc);
549 gst_object_unref (playsink->audio_tee_asrc);
550 playsink->audio_tee_asrc = NULL;
553 g_free (playsink->font_desc);
554 playsink->font_desc = NULL;
556 g_free (playsink->subtitle_encoding);
557 playsink->subtitle_encoding = NULL;
559 playsink->stream_synchronizer = NULL;
561 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
565 gst_play_sink_finalize (GObject * object)
567 GstPlaySink *playsink;
569 playsink = GST_PLAY_SINK (object);
571 g_static_rec_mutex_free (&playsink->lock);
573 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
577 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
580 GstElement **elem = NULL, *old = NULL;
582 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
584 GST_PLAY_SINK_LOCK (playsink);
586 case GST_PLAY_SINK_TYPE_AUDIO:
587 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
588 elem = &playsink->audio_sink;
590 case GST_PLAY_SINK_TYPE_VIDEO:
591 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
592 elem = &playsink->video_sink;
594 case GST_PLAY_SINK_TYPE_TEXT:
595 elem = &playsink->text_sink;
603 gst_object_ref (sink);
606 GST_PLAY_SINK_UNLOCK (playsink);
610 gst_element_set_state (old, GST_STATE_NULL);
611 gst_object_unref (old);
616 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
618 GstElement *result = NULL;
619 GstElement *elem = NULL, *chainp = NULL;
621 GST_PLAY_SINK_LOCK (playsink);
623 case GST_PLAY_SINK_TYPE_AUDIO:
624 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
626 GstPlayAudioChain *chain;
627 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
628 chainp = chain->sink;
629 elem = playsink->audio_sink;
632 case GST_PLAY_SINK_TYPE_VIDEO:
633 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
635 GstPlayVideoChain *chain;
636 if ((chain = (GstPlayVideoChain *) playsink->videochain))
637 chainp = chain->sink;
638 elem = playsink->video_sink;
641 case GST_PLAY_SINK_TYPE_TEXT:
643 GstPlayTextChain *chain;
644 if ((chain = (GstPlayTextChain *) playsink->textchain))
645 chainp = chain->sink;
646 elem = playsink->text_sink;
653 /* we have an active chain with a sink, get the sink */
654 result = gst_object_ref (chainp);
656 /* nothing found, return last configured sink */
657 if (result == NULL && elem)
658 result = gst_object_ref (elem);
659 GST_PLAY_SINK_UNLOCK (playsink);
665 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
668 GstPlaySink *playsink;
670 playsink = GST_PLAY_SINK (user_data);
671 /* nothing to do here, we need a dummy callback here to make the async call
673 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
677 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
680 GstPlaySink *playsink;
681 GstPlayVisChain *chain;
683 playsink = GST_PLAY_SINK (user_data);
685 GST_PLAY_SINK_LOCK (playsink);
686 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
687 /* now try to change the plugin in the running vis chain */
688 if (!(chain = (GstPlayVisChain *) playsink->vischain))
691 /* unlink the old plugin and unghost the pad */
692 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
693 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
695 /* set the old plugin to NULL and remove */
696 gst_element_set_state (chain->vis, GST_STATE_NULL);
697 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
699 /* add new plugin and set state to playing */
700 chain->vis = playsink->visualisation;
701 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
702 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
705 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
706 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
709 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
710 GST_PAD_LINK_CHECK_NOTHING);
711 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
715 /* Unblock the pad */
716 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
718 GST_PLAY_SINK_UNLOCK (playsink);
722 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
724 GstPlayVisChain *chain;
726 /* setting NULL means creating the default vis plugin */
728 vis = gst_element_factory_make ("goom", "vis");
730 /* simply return if we don't have a vis plugin here */
734 GST_PLAY_SINK_LOCK (playsink);
735 /* first store the new vis */
736 if (playsink->visualisation)
737 gst_object_unref (playsink->visualisation);
739 gst_object_ref_sink (vis);
740 playsink->visualisation = vis;
742 /* now try to change the plugin in the running vis chain, if we have no chain,
743 * we don't bother, any future vis chain will be created with the new vis
745 if (!(chain = (GstPlayVisChain *) playsink->vischain))
748 /* block the pad, the next time the callback is called we can change the
749 * visualisation. It's possible that this never happens or that the pad was
750 * already blocked. If the callback never happens, we don't have new data so
751 * we don't need the new vis plugin. If the pad was already blocked, the
752 * function returns FALSE but the previous pad block will do the right thing
754 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
755 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
758 GST_PLAY_SINK_UNLOCK (playsink);
764 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
766 GstElement *result = NULL;
767 GstPlayVisChain *chain;
769 GST_PLAY_SINK_LOCK (playsink);
770 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
771 /* we have an active chain, get the sink */
773 result = gst_object_ref (chain->vis);
775 /* nothing found, return last configured sink */
776 if (result == NULL && playsink->visualisation)
777 result = gst_object_ref (playsink->visualisation);
778 GST_PLAY_SINK_UNLOCK (playsink);
784 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
786 GstPlayAudioChain *chain;
788 GST_PLAY_SINK_LOCK (playsink);
789 playsink->volume = volume;
790 chain = (GstPlayAudioChain *) playsink->audiochain;
791 if (chain && chain->volume) {
792 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
793 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
794 chain->mute, volume, playsink->mute);
795 /* if there is a mute element or we are not muted, set the volume */
796 if (chain->mute || !playsink->mute)
797 g_object_set (chain->volume, "volume", volume, NULL);
799 GST_LOG_OBJECT (playsink, "no volume element");
800 playsink->volume_changed = TRUE;
802 GST_PLAY_SINK_UNLOCK (playsink);
806 gst_play_sink_get_volume (GstPlaySink * playsink)
809 GstPlayAudioChain *chain;
811 GST_PLAY_SINK_LOCK (playsink);
812 chain = (GstPlayAudioChain *) playsink->audiochain;
813 result = playsink->volume;
814 if (chain && chain->volume) {
815 if (chain->mute || !playsink->mute) {
816 g_object_get (chain->volume, "volume", &result, NULL);
817 playsink->volume = result;
820 GST_PLAY_SINK_UNLOCK (playsink);
826 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
828 GstPlayAudioChain *chain;
830 GST_PLAY_SINK_LOCK (playsink);
831 playsink->mute = mute;
832 chain = (GstPlayAudioChain *) playsink->audiochain;
835 g_object_set (chain->mute, "mute", mute, NULL);
836 } else if (chain->volume) {
838 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
840 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
844 playsink->mute_changed = TRUE;
846 GST_PLAY_SINK_UNLOCK (playsink);
850 gst_play_sink_get_mute (GstPlaySink * playsink)
853 GstPlayAudioChain *chain;
855 GST_PLAY_SINK_LOCK (playsink);
856 chain = (GstPlayAudioChain *) playsink->audiochain;
857 if (chain && chain->mute) {
858 g_object_get (chain->mute, "mute", &result, NULL);
859 playsink->mute = result;
861 result = playsink->mute;
863 GST_PLAY_SINK_UNLOCK (playsink);
869 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
873 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
874 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
878 add_chain (GstPlayChain * chain, gboolean add)
880 if (chain->added == add)
884 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
886 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
887 /* we don't want to lose our sink status */
888 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
897 activate_chain (GstPlayChain * chain, gboolean activate)
901 if (chain->activated == activate)
904 GST_OBJECT_LOCK (chain->playsink);
905 state = GST_STATE_TARGET (chain->playsink);
906 GST_OBJECT_UNLOCK (chain->playsink);
909 gst_element_set_state (chain->bin, state);
911 gst_element_set_state (chain->bin, GST_STATE_NULL);
913 chain->activated = activate;
919 element_is_sink (GstElement * element)
923 GST_OBJECT_LOCK (element);
924 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
925 GST_OBJECT_UNLOCK (element);
927 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
932 element_has_property (GstElement * element, const gchar * pname, GType type)
936 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
939 GST_DEBUG_OBJECT (element, "no %s property", pname);
943 if (type == G_TYPE_INVALID || type == pspec->value_type ||
944 g_type_is_a (pspec->value_type, type)) {
945 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
946 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
950 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
951 "and we expected it to be of type %s", pname,
952 g_type_name (pspec->value_type), g_type_name (type));
959 const gchar *prop_name;
962 } FindPropertyHelper;
965 find_property (GstElement * element, FindPropertyHelper * helper)
967 if (helper->need_sink && !element_is_sink (element)) {
968 gst_object_unref (element);
972 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
973 gst_object_unref (element);
977 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
978 (helper->need_sink) ? "sink" : "element");
979 return 0; /* keep it */
982 /* FIXME: why not move these functions into core? */
983 /* find a sink in the hierarchy with a property named @name. This function does
984 * not increase the refcount of the returned object and thus remains valid as
985 * long as the bin is valid. */
987 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
988 const gchar * name, GType expected_type)
990 GstElement *result = NULL;
993 if (element_has_property (obj, name, expected_type)) {
995 } else if (GST_IS_BIN (obj)) {
996 FindPropertyHelper helper = { name, expected_type, TRUE };
998 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
999 result = gst_iterator_find_custom (it,
1000 (GCompareFunc) find_property, &helper);
1001 gst_iterator_free (it);
1002 /* we don't need the extra ref */
1004 gst_object_unref (result);
1009 /* find an object in the hierarchy with a property named @name */
1011 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1012 const gchar * name, GType expected_type)
1014 GstElement *result = NULL;
1017 if (GST_IS_BIN (obj)) {
1018 FindPropertyHelper helper = { name, expected_type, FALSE };
1020 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1021 result = gst_iterator_find_custom (it,
1022 (GCompareFunc) find_property, &helper);
1023 gst_iterator_free (it);
1025 if (element_has_property (obj, name, expected_type)) {
1027 gst_object_ref (obj);
1034 do_async_start (GstPlaySink * playsink)
1036 GstMessage *message;
1038 if (!playsink->need_async_start) {
1039 GST_INFO_OBJECT (playsink, "no async_start needed");
1043 playsink->async_pending = TRUE;
1045 GST_INFO_OBJECT (playsink, "Sending async_start message");
1046 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1047 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1048 (playsink), message);
1052 do_async_done (GstPlaySink * playsink)
1054 GstMessage *message;
1056 if (playsink->async_pending) {
1057 GST_INFO_OBJECT (playsink, "Sending async_done message");
1058 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1059 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1060 (playsink), message);
1062 playsink->async_pending = FALSE;
1065 playsink->need_async_start = FALSE;
1068 /* try to change the state of an element. This function returns the element when
1069 * the state change could be performed. When this function returns NULL an error
1070 * occured and the element is unreffed if @unref is TRUE. */
1072 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1074 GstStateChangeReturn ret;
1077 ret = gst_element_set_state (element, GST_STATE_READY);
1078 if (ret == GST_STATE_CHANGE_FAILURE) {
1079 GST_DEBUG_OBJECT (playsink, "failed state change..");
1080 gst_element_set_state (element, GST_STATE_NULL);
1082 gst_object_unref (element);
1089 /* make the element (bin) that contains the elements needed to perform
1090 * video display. Only used for *raw* video streams.
1092 * +------------------------------------------------------------+
1094 * | +-------+ +----------+ +----------+ +---------+ |
1095 * | | queue | |colorspace| |videoscale| |videosink| |
1096 * | +-sink src-sink src-sink src-sink | |
1097 * | | +-------+ +----------+ +----------+ +---------+ |
1099 * +------------------------------------------------------------+
1102 static GstPlayVideoDeinterlaceChain *
1103 gen_video_deinterlace_chain (GstPlaySink * playsink)
1105 GstPlayVideoDeinterlaceChain *chain;
1108 GstElement *head = NULL, *prev = NULL;
1110 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1111 chain->chain.playsink = playsink;
1113 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1115 /* create a bin to hold objects, as we create them we add them to this bin so
1116 * that when something goes wrong we only need to unref the bin */
1117 chain->chain.bin = gst_bin_new ("vdbin");
1118 bin = GST_BIN_CAST (chain->chain.bin);
1119 gst_object_ref_sink (bin);
1121 GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
1122 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vdconv");
1123 if (chain->conv == NULL) {
1124 post_missing_element_message (playsink, "ffmpegcolorspace");
1125 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1126 (_("Missing element '%s' - check your GStreamer installation."),
1127 "ffmpegcolorspace"), ("video rendering might fail"));
1129 gst_bin_add (bin, chain->conv);
1134 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1135 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1136 if (chain->deinterlace == NULL) {
1137 post_missing_element_message (playsink, "deinterlace");
1138 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1139 (_("Missing element '%s' - check your GStreamer installation."),
1140 "deinterlace"), ("deinterlacing won't work"));
1142 gst_bin_add (bin, chain->deinterlace);
1144 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1145 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1148 head = chain->deinterlace;
1150 prev = chain->deinterlace;
1154 pad = gst_element_get_static_pad (head, "sink");
1155 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1156 gst_object_unref (pad);
1158 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1162 pad = gst_element_get_static_pad (prev, "src");
1163 chain->srcpad = gst_ghost_pad_new ("src", pad);
1164 gst_object_unref (pad);
1166 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1169 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1170 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1176 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1177 (NULL), ("Failed to configure the video deinterlace chain."));
1178 free_chain ((GstPlayChain *) chain);
1183 /* make the element (bin) that contains the elements needed to perform
1186 * +------------------------------------------------------------+
1188 * | +-------+ +----------+ +----------+ +---------+ |
1189 * | | queue | |colorspace| |videoscale| |videosink| |
1190 * | +-sink src-sink src-sink src-sink | |
1191 * | | +-------+ +----------+ +----------+ +---------+ |
1193 * +------------------------------------------------------------+
1196 static GstPlayVideoChain *
1197 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1199 GstPlayVideoChain *chain;
1202 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1204 chain = g_new0 (GstPlayVideoChain, 1);
1205 chain->chain.playsink = playsink;
1206 chain->chain.raw = raw;
1208 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1210 if (playsink->video_sink) {
1211 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1212 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1214 /* only try fallback if no specific sink was chosen */
1215 if (chain->sink == NULL) {
1216 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1217 elem = gst_element_factory_make ("autovideosink", "videosink");
1218 chain->sink = try_element (playsink, elem, TRUE);
1220 if (chain->sink == NULL) {
1221 /* if default sink from config.h is different then try it too */
1222 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1223 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1224 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1225 chain->sink = try_element (playsink, elem, TRUE);
1229 playsink->video_sink = gst_object_ref (chain->sink);
1231 if (chain->sink == NULL)
1235 /* if we can disable async behaviour of the sink, we can avoid adding a
1236 * queue for the audio chain. */
1238 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1241 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1242 async, GST_ELEMENT_NAME (elem));
1243 g_object_set (elem, "async", async, NULL);
1244 chain->async = async;
1246 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1247 chain->async = TRUE;
1250 /* find ts-offset element */
1252 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1255 /* create a bin to hold objects, as we create them we add them to this bin so
1256 * that when something goes wrong we only need to unref the bin */
1257 chain->chain.bin = gst_bin_new ("vbin");
1258 bin = GST_BIN_CAST (chain->chain.bin);
1259 gst_object_ref_sink (bin);
1260 gst_bin_add (bin, chain->sink);
1262 /* decouple decoder from sink, this improves playback quite a lot since the
1263 * decoder can continue while the sink blocks for synchronisation. We don't
1264 * need a lot of buffers as this consumes a lot of memory and we don't want
1265 * too little because else we would be context switching too quickly. */
1266 chain->queue = gst_element_factory_make ("queue", "vqueue");
1267 if (chain->queue == NULL) {
1268 post_missing_element_message (playsink, "queue");
1269 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1270 (_("Missing element '%s' - check your GStreamer installation."),
1271 "queue"), ("video rendering might be suboptimal"));
1275 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1276 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1277 gst_bin_add (bin, chain->queue);
1278 head = prev = chain->queue;
1281 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1282 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1284 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1285 gst_bin_add (bin, chain->conv);
1287 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1288 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1297 GST_DEBUG_OBJECT (playsink, "linking to sink");
1298 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1299 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1303 pad = gst_element_get_static_pad (head, "sink");
1304 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1305 gst_object_unref (pad);
1307 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1314 if (!elem && !playsink->video_sink) {
1315 post_missing_element_message (playsink, "autovideosink");
1316 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1317 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1318 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1319 (_("Both autovideosink and %s elements are missing."),
1320 DEFAULT_VIDEOSINK), (NULL));
1322 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1323 (_("The autovideosink element is missing.")), (NULL));
1326 if (playsink->video_sink) {
1327 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1328 (_("Configured videosink %s is not working."),
1329 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1330 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1331 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1332 (_("Both autovideosink and %s elements are not working."),
1333 DEFAULT_VIDEOSINK), (NULL));
1335 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1336 (_("The autovideosink element is not working.")), (NULL));
1339 free_chain ((GstPlayChain *) chain);
1344 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1345 (NULL), ("Failed to configure the video sink."));
1346 /* checking sink made it READY */
1347 gst_element_set_state (chain->sink, GST_STATE_NULL);
1348 /* Remove chain from the bin to allow reuse later */
1349 gst_bin_remove (bin, chain->sink);
1350 free_chain ((GstPlayChain *) chain);
1356 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1359 GstPlayVideoChain *chain;
1360 GstStateChangeReturn ret;
1362 chain = playsink->videochain;
1364 chain->chain.raw = raw;
1366 /* if the chain was active we don't do anything */
1367 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1370 /* try to set the sink element to READY again */
1371 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1372 if (ret == GST_STATE_CHANGE_FAILURE)
1375 /* find ts-offset element */
1377 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1380 /* if we can disable async behaviour of the sink, we can avoid adding a
1381 * queue for the audio chain. */
1383 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1386 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1387 async, GST_ELEMENT_NAME (elem));
1388 g_object_set (elem, "async", async, NULL);
1389 chain->async = async;
1391 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1392 chain->async = TRUE;
1397 /* make an element for playback of video with subtitles embedded.
1398 * Only used for *raw* video streams.
1400 * +--------------------------------------------+
1402 * | +--------+ +-----------------+ |
1403 * | | queue | | subtitleoverlay | |
1404 * video--src sink---video_sink | |
1405 * | +--------+ | src--src
1406 * text------------------text_sink | |
1407 * | +-----------------+ |
1408 * +--------------------------------------------+
1411 static GstPlayTextChain *
1412 gen_text_chain (GstPlaySink * playsink)
1414 GstPlayTextChain *chain;
1417 GstPad *videosinkpad, *textsinkpad, *srcpad;
1419 chain = g_new0 (GstPlayTextChain, 1);
1420 chain->chain.playsink = playsink;
1422 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1424 chain->chain.bin = gst_bin_new ("tbin");
1425 bin = GST_BIN_CAST (chain->chain.bin);
1426 gst_object_ref_sink (bin);
1428 videosinkpad = textsinkpad = srcpad = NULL;
1430 /* first try to hook the text pad to the custom sink */
1431 if (playsink->text_sink) {
1432 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1433 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1436 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1439 /* make sure the sparse subtitles don't participate in the preroll */
1440 g_object_set (elem, "async", FALSE, NULL);
1441 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1442 gst_bin_add (bin, chain->sink);
1443 /* NOTE streamsynchronizer needs streams decoupled */
1444 /* make a little queue */
1445 chain->queue = gst_element_factory_make ("queue", "subqueue");
1446 if (chain->queue == NULL) {
1447 post_missing_element_message (playsink, "queue");
1448 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1449 (_("Missing element '%s' - check your GStreamer installation."),
1450 "queue"), ("rendering might be suboptimal"));
1452 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1453 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1454 "silent", TRUE, NULL);
1455 gst_bin_add (bin, chain->queue);
1457 /* we have a custom sink, this will be our textsinkpad */
1458 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1459 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1460 /* we're all fine now and we can add the sink to the chain */
1461 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1462 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1464 GST_WARNING_OBJECT (playsink,
1465 "can't find a sink pad on custom text sink");
1466 gst_bin_remove (bin, chain->sink);
1467 gst_bin_remove (bin, chain->queue);
1469 chain->queue = NULL;
1471 /* try to set sync to true but it's no biggie when we can't */
1473 gst_play_sink_find_property_sinks (playsink, chain->sink,
1474 "sync", G_TYPE_BOOLEAN)))
1475 g_object_set (elem, "sync", TRUE, NULL);
1478 gst_bin_remove (bin, chain->sink);
1480 GST_WARNING_OBJECT (playsink,
1481 "can't find async property in custom text sink");
1484 if (textsinkpad == NULL) {
1485 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1486 (_("Custom text sink element is not usable.")),
1487 ("fallback to default textoverlay"));
1491 if (textsinkpad == NULL) {
1492 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1493 /* make a little queue */
1494 chain->queue = gst_element_factory_make ("queue", "vqueue");
1495 if (chain->queue == NULL) {
1496 post_missing_element_message (playsink, "queue");
1497 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1498 (_("Missing element '%s' - check your GStreamer installation."),
1499 "queue"), ("video rendering might be suboptimal"));
1501 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1502 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1503 "silent", TRUE, NULL);
1504 gst_bin_add (bin, chain->queue);
1505 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1509 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1510 if (chain->overlay == NULL) {
1511 post_missing_element_message (playsink, "subtitleoverlay");
1512 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1513 (_("Missing element '%s' - check your GStreamer installation."),
1514 "subtitleoverlay"), ("subtitle rendering disabled"));
1516 GstElement *element;
1518 gst_bin_add (bin, chain->overlay);
1520 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1521 if (playsink->font_desc) {
1522 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1523 playsink->font_desc, NULL);
1525 if (playsink->subtitle_encoding) {
1526 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1527 playsink->subtitle_encoding, NULL);
1530 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1531 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1533 /* make another little queue to decouple streams */
1534 element = gst_element_factory_make ("queue", "subqueue");
1535 if (element == NULL) {
1536 post_missing_element_message (playsink, "queue");
1537 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1538 (_("Missing element '%s' - check your GStreamer installation."),
1539 "queue"), ("rendering might be suboptimal"));
1541 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1542 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1543 "silent", TRUE, NULL);
1544 gst_bin_add (bin, element);
1545 gst_element_link_pads_full (element, "src", chain->overlay,
1546 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1547 textsinkpad = gst_element_get_static_pad (element, "sink");
1548 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1554 if (videosinkpad == NULL) {
1555 /* if we still don't have a videosink, we don't have an overlay. the only
1556 * thing we can do is insert an identity and ghost the src
1558 chain->identity = gst_element_factory_make ("identity", "tidentity");
1559 if (chain->identity == NULL) {
1560 post_missing_element_message (playsink, "identity");
1561 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1562 (_("Missing element '%s' - check your GStreamer installation."),
1563 "identity"), (NULL));
1565 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1566 g_object_set (chain->identity, "silent", TRUE, NULL);
1567 gst_bin_add (bin, chain->identity);
1568 srcpad = gst_element_get_static_pad (chain->identity, "src");
1569 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1573 /* expose the ghostpads */
1575 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1576 gst_object_unref (videosinkpad);
1577 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1580 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1581 gst_object_unref (textsinkpad);
1582 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1585 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1586 gst_object_unref (srcpad);
1587 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1594 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1598 g_object_get (object, "volume", &vol, NULL);
1599 playsink->volume = vol;
1601 g_object_notify (G_OBJECT (playsink), "volume");
1605 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1609 g_object_get (object, "mute", &mute, NULL);
1610 playsink->mute = mute;
1612 g_object_notify (G_OBJECT (playsink), "mute");
1615 /* make the chain that contains the elements needed to perform
1618 * We add a tee as the first element so that we can link the visualisation chain
1619 * to it when requested.
1621 * +-------------------------------------------------------------+
1623 * | +---------+ +----------+ +---------+ +---------+ |
1624 * | |audioconv| |audioscale| | volume | |audiosink| |
1625 * | +-srck src-sink src-sink src-sink | |
1626 * | | +---------+ +----------+ +---------+ +---------+ |
1628 * +-------------------------------------------------------------+
1630 static GstPlayAudioChain *
1631 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1633 GstPlayAudioChain *chain;
1635 gboolean have_volume;
1637 GstElement *head, *prev, *elem = NULL;
1639 chain = g_new0 (GstPlayAudioChain, 1);
1640 chain->chain.playsink = playsink;
1641 chain->chain.raw = raw;
1643 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1645 if (playsink->audio_sink) {
1646 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1647 playsink->audio_sink);
1648 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1650 /* only try fallback if no specific sink was chosen */
1651 if (chain->sink == NULL) {
1652 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1653 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1654 chain->sink = try_element (playsink, elem, TRUE);
1656 if (chain->sink == NULL) {
1657 /* if default sink from config.h is different then try it too */
1658 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1659 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1660 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1661 chain->sink = try_element (playsink, elem, TRUE);
1665 playsink->audio_sink = gst_object_ref (chain->sink);
1667 if (chain->sink == NULL)
1670 chain->chain.bin = gst_bin_new ("abin");
1671 bin = GST_BIN_CAST (chain->chain.bin);
1672 gst_object_ref_sink (bin);
1673 gst_bin_add (bin, chain->sink);
1675 /* we have to add a queue when we need to decouple for the video sink in
1676 * visualisations and for streamsynchronizer */
1677 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1678 chain->queue = gst_element_factory_make ("queue", "aqueue");
1679 if (chain->queue == NULL) {
1680 post_missing_element_message (playsink, "queue");
1681 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1682 (_("Missing element '%s' - check your GStreamer installation."),
1683 "queue"), ("audio playback and visualizations might not work"));
1687 g_object_set (chain->queue, "silent", TRUE, NULL);
1688 gst_bin_add (bin, chain->queue);
1689 prev = head = chain->queue;
1692 /* find ts-offset element */
1694 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1697 /* check if the sink, or something within the sink, has the volume property.
1698 * If it does we don't need to add a volume element. */
1700 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1703 chain->volume = elem;
1705 g_signal_connect (chain->volume, "notify::volume",
1706 G_CALLBACK (notify_volume_cb), playsink);
1708 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1710 chain->sink_volume = TRUE;
1711 /* if the sink also has a mute property we can use this as well. We'll only
1712 * use the mute property if there is a volume property. We can simulate the
1713 * mute with the volume otherwise. */
1715 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1718 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1719 g_signal_connect (chain->mute, "notify::mute",
1720 G_CALLBACK (notify_mute_cb), playsink);
1722 /* use the sink to control the volume and mute */
1723 if (playsink->volume_changed) {
1724 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1725 playsink->volume_changed = FALSE;
1727 if (playsink->mute_changed) {
1729 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1732 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1734 playsink->mute_changed = FALSE;
1737 /* no volume, we need to add a volume element when we can */
1738 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1739 have_volume = FALSE;
1740 chain->sink_volume = FALSE;
1743 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1744 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1745 GST_DEBUG_OBJECT (playsink, "creating audioconvert");
1747 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL);
1748 gst_bin_add (bin, chain->conv);
1750 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1751 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1758 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_converters =
1759 !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1760 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = (!have_volume
1761 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
1763 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1764 GstPlaySinkAudioConvert *conv =
1765 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1768 chain->volume = conv->volume;
1771 g_signal_connect (chain->volume, "notify::volume",
1772 G_CALLBACK (notify_volume_cb), playsink);
1774 /* volume also has the mute property */
1775 chain->mute = chain->volume;
1776 g_signal_connect (chain->mute, "notify::mute",
1777 G_CALLBACK (notify_mute_cb), playsink);
1779 /* configure with the latest volume and mute */
1780 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1782 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1788 /* we only have to link to the previous element if we have something in
1789 * front of the sink */
1790 GST_DEBUG_OBJECT (playsink, "linking to sink");
1791 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1792 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1796 /* post a warning if we have no way to configure the volume */
1798 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1799 (_("No volume control found")), ("Volume/mute is not available"));
1802 /* and ghost the sinkpad of the headmost element */
1803 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1804 pad = gst_element_get_static_pad (head, "sink");
1805 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1806 gst_object_unref (pad);
1807 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1814 if (!elem && !playsink->audio_sink) {
1815 post_missing_element_message (playsink, "autoaudiosink");
1816 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1817 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1818 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1819 (_("Both autoaudiosink and %s elements are missing."),
1820 DEFAULT_AUDIOSINK), (NULL));
1822 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1823 (_("The autoaudiosink element is missing.")), (NULL));
1826 if (playsink->audio_sink) {
1827 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1828 (_("Configured audiosink %s is not working."),
1829 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1830 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1831 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1832 (_("Both autoaudiosink and %s elements are not working."),
1833 DEFAULT_AUDIOSINK), (NULL));
1835 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1836 (_("The autoaudiosink element is not working.")), (NULL));
1839 free_chain ((GstPlayChain *) chain);
1844 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1845 (NULL), ("Failed to configure the audio sink."));
1846 /* checking sink made it READY */
1847 gst_element_set_state (chain->sink, GST_STATE_NULL);
1848 /* Remove chain from the bin to allow reuse later */
1849 gst_bin_remove (bin, chain->sink);
1850 free_chain ((GstPlayChain *) chain);
1856 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1859 GstPlayAudioChain *chain;
1860 GstStateChangeReturn ret;
1862 chain = playsink->audiochain;
1864 chain->chain.raw = raw;
1866 /* if the chain was active we don't do anything */
1867 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1870 /* try to set the sink element to READY again */
1871 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1872 if (ret == GST_STATE_CHANGE_FAILURE)
1875 /* find ts-offset element */
1877 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1880 /* check if the sink, or something within the sink, has the volume property.
1881 * If it does we don't need to add a volume element. */
1883 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1886 chain->volume = elem;
1888 if (playsink->volume_changed) {
1889 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1891 /* use the sink to control the volume */
1892 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1893 playsink->volume_changed = FALSE;
1896 g_signal_connect (chain->volume, "notify::volume",
1897 G_CALLBACK (notify_volume_cb), playsink);
1898 /* if the sink also has a mute property we can use this as well. We'll only
1899 * use the mute property if there is a volume property. We can simulate the
1900 * mute with the volume otherwise. */
1902 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1905 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1906 g_signal_connect (chain->mute, "notify::mute",
1907 G_CALLBACK (notify_mute_cb), playsink);
1910 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE;
1912 GstPlaySinkAudioConvert *conv =
1913 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1915 /* no volume, we need to add a volume element when we can */
1916 conv->use_volume = TRUE;
1917 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1919 /* Disconnect signals */
1920 disconnect_chain (chain, playsink);
1923 chain->volume = conv->volume;
1924 chain->mute = chain->volume;
1926 g_signal_connect (chain->volume, "notify::volume",
1927 G_CALLBACK (notify_volume_cb), playsink);
1929 g_signal_connect (chain->mute, "notify::mute",
1930 G_CALLBACK (notify_mute_cb), playsink);
1932 /* configure with the latest volume and mute */
1933 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1934 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1937 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
1943 * +-------------------------------------------------------------------+
1945 * | +----------+ +------------+ +----------+ +-------+ |
1946 * | | visqueue | | audioconv | | audiores | | vis | |
1947 * | +-sink src-sink + samp src-sink src-sink src-+ |
1948 * | | +----------+ +------------+ +----------+ +-------+ | |
1950 * +-------------------------------------------------------------------+
1953 static GstPlayVisChain *
1954 gen_vis_chain (GstPlaySink * playsink)
1956 GstPlayVisChain *chain;
1962 chain = g_new0 (GstPlayVisChain, 1);
1963 chain->chain.playsink = playsink;
1965 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
1967 chain->chain.bin = gst_bin_new ("visbin");
1968 bin = GST_BIN_CAST (chain->chain.bin);
1969 gst_object_ref_sink (bin);
1971 /* we're queuing raw audio here, we can remove this queue when we can disable
1972 * async behaviour in the video sink. */
1973 chain->queue = gst_element_factory_make ("queue", "visqueue");
1974 if (chain->queue == NULL)
1976 g_object_set (chain->queue, "silent", TRUE, NULL);
1977 gst_bin_add (bin, chain->queue);
1979 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1980 if (chain->conv == NULL)
1981 goto no_audioconvert;
1982 gst_bin_add (bin, chain->conv);
1984 chain->resample = gst_element_factory_make ("audioresample", "aresample");
1985 if (chain->resample == NULL)
1986 goto no_audioresample;
1987 gst_bin_add (bin, chain->resample);
1989 /* this pad will be used for blocking the dataflow and switching the vis
1991 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
1993 if (playsink->visualisation) {
1994 GST_DEBUG_OBJECT (playsink, "trying configure vis");
1995 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
1997 if (chain->vis == NULL) {
1998 GST_DEBUG_OBJECT (playsink, "trying goom");
1999 elem = gst_element_factory_make ("goom", "vis");
2000 chain->vis = try_element (playsink, elem, TRUE);
2002 if (chain->vis == NULL)
2005 gst_bin_add (bin, chain->vis);
2007 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2008 GST_PAD_LINK_CHECK_NOTHING);
2010 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2011 GST_PAD_LINK_CHECK_NOTHING);
2013 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2014 GST_PAD_LINK_CHECK_NOTHING);
2018 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2019 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2021 pad = gst_element_get_static_pad (chain->queue, "sink");
2022 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2023 gst_object_unref (pad);
2024 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2026 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2027 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2034 post_missing_element_message (playsink, "queue");
2035 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2036 (_("Missing element '%s' - check your GStreamer installation."),
2038 free_chain ((GstPlayChain *) chain);
2043 post_missing_element_message (playsink, "audioconvert");
2044 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2045 (_("Missing element '%s' - check your GStreamer installation."),
2046 "audioconvert"), ("possibly a liboil version mismatch?"));
2047 free_chain ((GstPlayChain *) chain);
2052 post_missing_element_message (playsink, "audioresample");
2053 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2054 (_("Missing element '%s' - check your GStreamer installation."),
2055 "audioresample"), (NULL));
2056 free_chain ((GstPlayChain *) chain);
2061 post_missing_element_message (playsink, "goom");
2062 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2063 (_("Missing element '%s' - check your GStreamer installation."),
2065 free_chain ((GstPlayChain *) chain);
2070 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2071 (NULL), ("Failed to configure the visualisation element."));
2072 /* element made it to READY */
2073 gst_element_set_state (chain->vis, GST_STATE_NULL);
2074 free_chain ((GstPlayChain *) chain);
2079 /* this function is called when all the request pads are requested and when we
2080 * have to construct the final pipeline. Based on the flags we construct the
2081 * final output pipelines.
2084 gst_play_sink_reconfigure (GstPlaySink * playsink)
2087 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2089 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2091 /* assume we need nothing */
2092 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2094 GST_PLAY_SINK_LOCK (playsink);
2095 GST_OBJECT_LOCK (playsink);
2096 /* get flags, there are protected with the object lock */
2097 flags = playsink->flags;
2098 GST_OBJECT_UNLOCK (playsink);
2100 /* figure out which components we need */
2101 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2102 /* we have subtitles and we are requested to show it */
2106 if (((flags & GST_PLAY_FLAG_VIDEO)
2107 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2108 /* we have video and we are requested to show it */
2111 /* we only deinterlace if native video is not requested and
2112 * we have raw video */
2113 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2114 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2115 need_deinterlace = TRUE;
2118 if (playsink->audio_pad) {
2119 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2122 if (playsink->audio_pad_raw) {
2123 /* only can do vis with raw uncompressed audio */
2124 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2125 /* also add video when we add visualisation */
2132 /* we have a text_pad and we need text rendering, in this case we need a
2133 * video_pad to combine the video with the text or visualizations */
2134 if (need_text && !need_video) {
2135 if (playsink->video_pad) {
2137 } else if (need_audio) {
2138 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2139 (_("Can't play a text file without video or visualizations.")),
2140 ("Have text pad but no video pad or visualizations"));
2143 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2144 (_("Can't play a text file without video or visualizations.")),
2145 ("Have text pad but no video pad or visualizations"));
2146 GST_PLAY_SINK_UNLOCK (playsink);
2151 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2152 need_video, need_vis, need_text);
2154 /* set up video pipeline */
2156 gboolean raw, async;
2158 /* we need a raw sink when we do vis or when we have a raw pad */
2159 raw = need_vis ? TRUE : playsink->video_pad_raw;
2160 /* we try to set the sink async=FALSE when we need vis, this way we can
2161 * avoid a queue in the audio chain. */
2164 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2165 playsink->video_pad_raw);
2167 if (playsink->videochain) {
2168 /* try to reactivate the chain */
2169 if (!setup_video_chain (playsink, raw, async)) {
2170 if (playsink->video_sinkpad_stream_synchronizer) {
2171 gst_element_release_request_pad (GST_ELEMENT_CAST
2172 (playsink->stream_synchronizer),
2173 playsink->video_sinkpad_stream_synchronizer);
2174 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2175 playsink->video_sinkpad_stream_synchronizer = NULL;
2176 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2177 playsink->video_srcpad_stream_synchronizer = NULL;
2180 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2182 /* Remove the sink from the bin to keep its state
2183 * and unparent it to allow reuse */
2184 if (playsink->videochain->sink)
2185 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2186 playsink->videochain->sink);
2188 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2189 free_chain ((GstPlayChain *) playsink->videochain);
2190 playsink->videochain = NULL;
2194 if (!playsink->videochain)
2195 playsink->videochain = gen_video_chain (playsink, raw, async);
2196 if (!playsink->videochain)
2199 if (!playsink->video_sinkpad_stream_synchronizer) {
2202 playsink->video_sinkpad_stream_synchronizer =
2203 gst_element_get_request_pad (GST_ELEMENT_CAST
2204 (playsink->stream_synchronizer), "sink_%d");
2205 it = gst_pad_iterate_internal_links
2206 (playsink->video_sinkpad_stream_synchronizer);
2208 gst_iterator_next (it,
2209 (gpointer *) & playsink->video_srcpad_stream_synchronizer);
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) {
2358 playsink->audio_sinkpad_stream_synchronizer =
2359 gst_element_get_request_pad (GST_ELEMENT_CAST
2360 (playsink->stream_synchronizer), "sink_%d");
2361 it = gst_pad_iterate_internal_links
2362 (playsink->audio_sinkpad_stream_synchronizer);
2364 gst_iterator_next (it,
2365 (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2366 g_assert (playsink->audio_srcpad_stream_synchronizer);
2367 gst_iterator_free (it);
2370 if (playsink->audiochain) {
2371 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2372 if (playsink->audio_tee_asrc == NULL) {
2373 playsink->audio_tee_asrc =
2374 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2376 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2377 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2378 gst_pad_link_full (playsink->audio_tee_asrc,
2379 playsink->audio_sinkpad_stream_synchronizer,
2380 GST_PAD_LINK_CHECK_NOTHING);
2381 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2382 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2385 GST_DEBUG_OBJECT (playsink, "no audio needed");
2386 /* we have no audio or we are requested to not play audio */
2387 if (playsink->audiochain) {
2388 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2389 /* release the audio pad */
2390 if (playsink->audio_tee_asrc) {
2391 gst_element_release_request_pad (playsink->audio_tee,
2392 playsink->audio_tee_asrc);
2393 gst_object_unref (playsink->audio_tee_asrc);
2394 playsink->audio_tee_asrc = NULL;
2397 if (playsink->audio_sinkpad_stream_synchronizer) {
2398 gst_element_release_request_pad (GST_ELEMENT_CAST
2399 (playsink->stream_synchronizer),
2400 playsink->audio_sinkpad_stream_synchronizer);
2401 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2402 playsink->audio_sinkpad_stream_synchronizer = NULL;
2403 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2404 playsink->audio_srcpad_stream_synchronizer = NULL;
2407 if (playsink->audiochain->sink_volume) {
2408 disconnect_chain (playsink->audiochain, playsink);
2409 playsink->audiochain->volume = NULL;
2410 playsink->audiochain->mute = NULL;
2411 playsink->audiochain->ts_offset = NULL;
2413 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2414 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2421 if (!playsink->vischain)
2422 playsink->vischain = gen_vis_chain (playsink);
2424 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2426 if (playsink->vischain) {
2427 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2429 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2430 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2431 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2432 if (playsink->audio_tee_vissrc == NULL) {
2433 playsink->audio_tee_vissrc =
2434 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2436 gst_pad_link_full (playsink->audio_tee_vissrc,
2437 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2438 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2439 GST_PAD_LINK_CHECK_NOTHING);
2440 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2441 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2442 gst_object_unref (srcpad);
2445 GST_DEBUG_OBJECT (playsink, "no vis needed");
2446 if (playsink->vischain) {
2447 if (playsink->audio_tee_vissrc) {
2448 gst_element_release_request_pad (playsink->audio_tee,
2449 playsink->audio_tee_vissrc);
2450 gst_object_unref (playsink->audio_tee_vissrc);
2451 playsink->audio_tee_vissrc = NULL;
2453 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2454 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2455 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2460 GST_DEBUG_OBJECT (playsink, "adding text");
2461 if (!playsink->textchain) {
2462 GST_DEBUG_OBJECT (playsink, "creating text chain");
2463 playsink->textchain = gen_text_chain (playsink);
2465 if (playsink->textchain) {
2468 GST_DEBUG_OBJECT (playsink, "adding text chain");
2469 if (playsink->textchain->overlay)
2470 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2472 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2474 if (!playsink->text_sinkpad_stream_synchronizer) {
2475 playsink->text_sinkpad_stream_synchronizer =
2476 gst_element_get_request_pad (GST_ELEMENT_CAST
2477 (playsink->stream_synchronizer), "sink_%d");
2478 it = gst_pad_iterate_internal_links
2479 (playsink->text_sinkpad_stream_synchronizer);
2481 gst_iterator_next (it,
2482 (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2483 g_assert (playsink->text_srcpad_stream_synchronizer);
2484 gst_iterator_free (it);
2486 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2487 playsink->text_sinkpad_stream_synchronizer);
2488 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2489 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2496 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2497 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2498 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2499 GST_PAD_LINK_CHECK_NOTHING);
2500 gst_object_unref (srcpad);
2502 if (need_deinterlace)
2503 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2504 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2506 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2507 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2509 gst_pad_link_full (playsink->textchain->srcpad,
2510 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2512 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2515 GST_DEBUG_OBJECT (playsink, "no text needed");
2516 /* we have no subtitles/text or we are requested to not show them */
2518 if (playsink->text_sinkpad_stream_synchronizer) {
2519 gst_element_release_request_pad (GST_ELEMENT_CAST
2520 (playsink->stream_synchronizer),
2521 playsink->text_sinkpad_stream_synchronizer);
2522 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2523 playsink->text_sinkpad_stream_synchronizer = NULL;
2524 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2525 playsink->text_srcpad_stream_synchronizer = NULL;
2528 if (playsink->textchain) {
2529 if (playsink->text_pad == NULL) {
2530 /* no text pad, remove the chain entirely */
2531 GST_DEBUG_OBJECT (playsink, "removing text chain");
2532 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2533 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2535 /* we have a chain and a textpad, turn the subtitles off */
2536 GST_DEBUG_OBJECT (playsink, "turning off the text");
2537 if (playsink->textchain->overlay)
2538 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2542 if (!need_video && playsink->video_pad) {
2543 if (playsink->video_sinkpad_stream_synchronizer) {
2544 gst_element_release_request_pad (GST_ELEMENT_CAST
2545 (playsink->stream_synchronizer),
2546 playsink->video_sinkpad_stream_synchronizer);
2547 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2548 playsink->video_sinkpad_stream_synchronizer = NULL;
2549 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2550 playsink->video_srcpad_stream_synchronizer = NULL;
2553 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2556 if (playsink->text_pad && !playsink->textchain)
2557 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2559 update_av_offset (playsink);
2560 do_async_done (playsink);
2561 GST_PLAY_SINK_UNLOCK (playsink);
2568 /* gen_ chain already posted error */
2569 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2570 GST_PLAY_SINK_UNLOCK (playsink);
2576 * gst_play_sink_set_flags:
2577 * @playsink: a #GstPlaySink
2578 * @flags: #GstPlayFlags
2580 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2581 * when constructing the sink pipelins.
2583 * Returns: TRUE if the flags could be configured.
2586 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2588 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2590 GST_OBJECT_LOCK (playsink);
2591 playsink->flags = flags;
2592 GST_OBJECT_UNLOCK (playsink);
2598 * gst_play_sink_get_flags:
2599 * @playsink: a #GstPlaySink
2601 * Get the flags of @playsink. That flags control the behaviour of the sink when
2602 * it constructs the sink pipelines.
2604 * Returns: the currently configured #GstPlayFlags.
2607 gst_play_sink_get_flags (GstPlaySink * playsink)
2611 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2613 GST_OBJECT_LOCK (playsink);
2614 res = playsink->flags;
2615 GST_OBJECT_UNLOCK (playsink);
2621 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2623 GstPlayTextChain *chain;
2625 GST_PLAY_SINK_LOCK (playsink);
2626 chain = (GstPlayTextChain *) playsink->textchain;
2627 g_free (playsink->font_desc);
2628 playsink->font_desc = g_strdup (desc);
2629 if (chain && chain->overlay) {
2630 g_object_set (chain->overlay, "font-desc", desc, NULL);
2632 GST_PLAY_SINK_UNLOCK (playsink);
2636 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2638 gchar *result = NULL;
2639 GstPlayTextChain *chain;
2641 GST_PLAY_SINK_LOCK (playsink);
2642 chain = (GstPlayTextChain *) playsink->textchain;
2643 if (chain && chain->overlay) {
2644 g_object_get (chain->overlay, "font-desc", &result, NULL);
2645 playsink->font_desc = g_strdup (result);
2647 result = g_strdup (playsink->font_desc);
2649 GST_PLAY_SINK_UNLOCK (playsink);
2655 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2656 const gchar * encoding)
2658 GstPlayTextChain *chain;
2660 GST_PLAY_SINK_LOCK (playsink);
2661 chain = (GstPlayTextChain *) playsink->textchain;
2662 g_free (playsink->subtitle_encoding);
2663 playsink->subtitle_encoding = g_strdup (encoding);
2664 if (chain && chain->overlay) {
2665 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2667 GST_PLAY_SINK_UNLOCK (playsink);
2671 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2673 gchar *result = NULL;
2674 GstPlayTextChain *chain;
2676 GST_PLAY_SINK_LOCK (playsink);
2677 chain = (GstPlayTextChain *) playsink->textchain;
2678 if (chain && chain->overlay) {
2679 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2680 playsink->subtitle_encoding = g_strdup (result);
2682 result = g_strdup (playsink->subtitle_encoding);
2684 GST_PLAY_SINK_UNLOCK (playsink);
2690 update_av_offset (GstPlaySink * playsink)
2693 GstPlayAudioChain *achain;
2694 GstPlayVideoChain *vchain;
2696 av_offset = playsink->av_offset;
2697 achain = (GstPlayAudioChain *) playsink->audiochain;
2698 vchain = (GstPlayVideoChain *) playsink->videochain;
2700 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2701 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2702 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2704 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2709 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2711 GST_PLAY_SINK_LOCK (playsink);
2712 playsink->av_offset = av_offset;
2713 update_av_offset (playsink);
2714 GST_PLAY_SINK_UNLOCK (playsink);
2718 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2722 GST_PLAY_SINK_LOCK (playsink);
2723 result = playsink->av_offset;
2724 GST_PLAY_SINK_UNLOCK (playsink);
2730 * gst_play_sink_get_last_frame:
2731 * @playsink: a #GstPlaySink
2733 * Get the last displayed frame from @playsink. This frame is in the native
2734 * format of the sink element, the caps on the result buffer contain the format
2735 * of the frame data.
2737 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2741 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2743 GstBuffer *result = NULL;
2744 GstPlayVideoChain *chain;
2746 GST_PLAY_SINK_LOCK (playsink);
2747 GST_DEBUG_OBJECT (playsink, "taking last frame");
2748 /* get the video chain if we can */
2749 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2750 GST_DEBUG_OBJECT (playsink, "found video chain");
2751 /* see if the chain is active */
2752 if (chain->chain.activated && chain->sink) {
2755 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2757 /* find and get the last-buffer property now */
2759 gst_play_sink_find_property (playsink, chain->sink,
2760 "last-buffer", GST_TYPE_BUFFER))) {
2761 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2762 g_object_get (elem, "last-buffer", &result, NULL);
2763 gst_object_unref (elem);
2767 GST_PLAY_SINK_UNLOCK (playsink);
2773 * gst_play_sink_convert_frame:
2774 * @playsink: a #GstPlaySink
2777 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2778 * be in the native format of the sink element and the caps on the buffer
2779 * describe the format of the frame. If @caps is not %NULL, the video
2780 * frame will be converted to the format of the caps.
2782 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2783 * available or when the conversion failed.
2786 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2790 result = gst_play_sink_get_last_frame (playsink);
2791 if (result != NULL && caps != NULL) {
2795 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2796 gst_buffer_unref (result);
2797 if (temp == NULL && err) {
2798 /* I'm really uncertain whether we should make playsink post an error
2799 * on the bus or not. It's not like it's a critical issue regarding
2800 * playsink behaviour. */
2801 GST_ERROR ("Error converting frame: %s", err->message);
2809 is_raw_structure (GstStructure * s)
2813 name = gst_structure_get_name (s);
2815 if (g_str_has_prefix (name, "video/x-raw-") ||
2816 g_str_has_prefix (name, "audio/x-raw-"))
2822 is_raw_pad (GstPad * pad)
2824 GstPad *peer = gst_pad_get_peer (pad);
2826 gboolean raw = TRUE;
2831 caps = gst_pad_get_negotiated_caps (peer);
2835 caps = gst_pad_get_caps_reffed (peer);
2837 n = gst_caps_get_size (caps);
2838 for (i = 0; i < n; i++) {
2839 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2843 } else if (raw != r) {
2844 GST_ERROR_OBJECT (pad,
2845 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2851 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2853 gst_caps_unref (caps);
2854 gst_object_unref (peer);
2860 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
2862 GstPlaySink *playsink = (GstPlaySink *) user_data;
2865 GST_PLAY_SINK_LOCK (playsink);
2867 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2868 if (pad == playsink->video_pad) {
2869 playsink->video_pad_blocked = blocked;
2870 GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
2871 } else if (pad == playsink->audio_pad) {
2872 playsink->audio_pad_blocked = blocked;
2873 GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
2874 } else if (pad == playsink->text_pad) {
2875 playsink->text_pad_blocked = blocked;
2876 GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
2880 gst_object_unref (pad);
2881 GST_PLAY_SINK_UNLOCK (playsink);
2885 if ((!playsink->video_pad || playsink->video_pad_blocked) &&
2886 (!playsink->audio_pad || playsink->audio_pad_blocked) &&
2887 (!playsink->text_pad || playsink->text_pad_blocked)) {
2888 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
2890 if (playsink->video_pad) {
2891 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
2892 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
2893 playsink->video_pad_raw);
2896 if (playsink->audio_pad) {
2897 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
2898 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
2899 playsink->audio_pad_raw);
2902 gst_play_sink_reconfigure (playsink);
2904 if (playsink->video_pad) {
2906 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2907 (playsink->video_pad)));
2908 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
2909 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2910 gst_object_unref (opad);
2913 if (playsink->audio_pad) {
2915 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2916 (playsink->audio_pad)));
2917 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
2918 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2919 gst_object_unref (opad);
2922 if (playsink->text_pad) {
2924 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2925 (playsink->text_pad)));
2926 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
2927 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2928 gst_object_unref (opad);
2932 gst_object_unref (pad);
2934 GST_PLAY_SINK_UNLOCK (playsink);
2938 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
2940 gboolean reconfigure = FALSE;
2944 g_object_get (pad, "caps", &caps, NULL);
2948 if (pad == playsink->audio_pad) {
2949 raw = is_raw_pad (pad);
2950 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
2951 && playsink->audiochain;
2952 GST_DEBUG_OBJECT (pad,
2953 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
2955 } else if (pad == playsink->video_pad) {
2956 raw = is_raw_pad (pad);
2957 reconfigure = (! !playsink->video_pad_raw != ! !raw)
2958 && playsink->videochain;
2959 GST_DEBUG_OBJECT (pad,
2960 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
2964 gst_caps_unref (caps);
2967 GST_PLAY_SINK_LOCK (playsink);
2968 if (playsink->video_pad) {
2970 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2971 (playsink->video_pad)));
2972 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
2973 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2974 gst_object_unref (opad);
2977 if (playsink->audio_pad) {
2979 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2980 (playsink->audio_pad)));
2981 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
2982 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2983 gst_object_unref (opad);
2986 if (playsink->text_pad) {
2988 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2989 (playsink->text_pad)));
2990 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
2991 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2992 gst_object_unref (opad);
2994 GST_PLAY_SINK_UNLOCK (playsink);
2999 * gst_play_sink_request_pad
3000 * @playsink: a #GstPlaySink
3001 * @type: a #GstPlaySinkType
3003 * Create or return a pad of @type.
3005 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3008 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3011 gboolean created = FALSE;
3012 gboolean activate = TRUE;
3013 const gchar *pad_name = NULL;
3015 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3017 GST_PLAY_SINK_LOCK (playsink);
3019 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3020 case GST_PLAY_SINK_TYPE_AUDIO:
3021 pad_name = "audio_sink";
3022 if (!playsink->audio_tee) {
3023 GST_LOG_OBJECT (playsink, "creating tee");
3024 /* create tee when needed. This element will feed the audio sink chain
3025 * and the vis chain. */
3026 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3027 if (playsink->audio_tee == NULL) {
3028 post_missing_element_message (playsink, "tee");
3029 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3030 (_("Missing element '%s' - check your GStreamer installation."),
3035 playsink->audio_tee_sink =
3036 gst_element_get_static_pad (playsink->audio_tee, "sink");
3037 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3038 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3041 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3043 if (!playsink->audio_pad) {
3044 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3045 playsink->audio_pad =
3046 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3047 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3048 G_CALLBACK (caps_notify_cb), playsink);
3051 playsink->audio_pad_raw = FALSE;
3052 res = playsink->audio_pad;
3054 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3055 case GST_PLAY_SINK_TYPE_VIDEO:
3056 pad_name = "video_sink";
3057 if (!playsink->video_pad) {
3058 GST_LOG_OBJECT (playsink, "ghosting videosink");
3059 playsink->video_pad =
3060 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3061 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3062 G_CALLBACK (caps_notify_cb), playsink);
3065 playsink->video_pad_raw = FALSE;
3066 res = playsink->video_pad;
3068 case GST_PLAY_SINK_TYPE_TEXT:
3069 GST_LOG_OBJECT (playsink, "ghosting text");
3070 if (!playsink->text_pad) {
3071 playsink->text_pad =
3072 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3075 res = playsink->text_pad;
3077 case GST_PLAY_SINK_TYPE_FLUSHING:
3081 /* we need a unique padname for the flushing pad. */
3082 padname = g_strdup_printf ("flushing_%d", playsink->count);
3083 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3094 GST_PLAY_SINK_UNLOCK (playsink);
3096 if (created && res) {
3097 /* we have to add the pad when it's active or we get an error when the
3098 * element is 'running' */
3099 gst_pad_set_active (res, TRUE);
3100 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3101 if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3103 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3105 gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3106 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3107 gst_object_unref (blockpad);
3110 gst_pad_set_active (res, activate);
3117 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3122 GstPlaySinkType type;
3123 const gchar *tplname;
3125 g_return_val_if_fail (templ != NULL, NULL);
3127 GST_DEBUG_OBJECT (element, "name:%s", name);
3129 psink = GST_PLAY_SINK (element);
3130 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3132 /* Figure out the GstPlaySinkType based on the template */
3133 if (!strcmp (tplname, "audio_sink"))
3134 type = GST_PLAY_SINK_TYPE_AUDIO;
3135 else if (!strcmp (tplname, "audio_raw_sink"))
3136 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3137 else if (!strcmp (tplname, "video_sink"))
3138 type = GST_PLAY_SINK_TYPE_VIDEO;
3139 else if (!strcmp (tplname, "video_raw_sink"))
3140 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3141 else if (!strcmp (tplname, "text_sink"))
3142 type = GST_PLAY_SINK_TYPE_TEXT;
3144 goto unknown_template;
3146 pad = gst_play_sink_request_pad (psink, type);
3150 GST_WARNING_OBJECT (element, "Unknown pad template");
3155 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3157 GstPad **res = NULL;
3158 gboolean untarget = TRUE;
3160 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3162 GST_PLAY_SINK_LOCK (playsink);
3163 if (pad == playsink->video_pad) {
3164 res = &playsink->video_pad;
3165 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3167 } else if (pad == playsink->audio_pad) {
3168 res = &playsink->audio_pad;
3169 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3171 } else if (pad == playsink->text_pad) {
3172 res = &playsink->text_pad;
3174 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3178 GST_PLAY_SINK_UNLOCK (playsink);
3181 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3182 gst_pad_set_active (*res, FALSE);
3184 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3185 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3187 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3188 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3194 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3196 GstPlaySink *psink = GST_PLAY_SINK (element);
3198 gst_play_sink_release_pad (psink, pad);
3202 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3204 GstPlaySink *playsink;
3206 playsink = GST_PLAY_SINK_CAST (bin);
3208 switch (GST_MESSAGE_TYPE (message)) {
3209 case GST_MESSAGE_STEP_DONE:
3214 gboolean flush, intermediate, eos;
3217 GST_INFO_OBJECT (playsink, "Handling step-done message");
3218 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3219 &intermediate, &duration, &eos);
3221 if (format == GST_FORMAT_BUFFERS) {
3222 /* for the buffer format, we align the other streams */
3223 if (playsink->audiochain) {
3227 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3230 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3231 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3235 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3239 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3244 /* Send an event to our sinks until one of them works; don't then send to the
3245 * remaining sinks (unlike GstBin)
3246 * Special case: If a text sink is set we need to send the event
3247 * to them in case it's source is different from the a/v stream's source.
3250 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3252 gboolean res = TRUE;
3254 if (playsink->textchain && playsink->textchain->sink) {
3255 gst_event_ref (event);
3256 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3257 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to text sink");
3259 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3263 if (playsink->videochain) {
3264 gst_event_ref (event);
3265 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3266 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
3269 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3271 if (playsink->audiochain) {
3272 gst_event_ref (event);
3273 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3274 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
3277 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3281 gst_event_unref (event);
3285 /* We only want to send the event to a single sink (overriding GstBin's
3286 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3287 * events appropriately. So, this is a messy duplication of code. */
3289 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3291 gboolean res = FALSE;
3292 GstEventType event_type = GST_EVENT_TYPE (event);
3293 GstPlaySink *playsink;
3295 playsink = GST_PLAY_SINK_CAST (element);
3297 switch (event_type) {
3298 case GST_EVENT_SEEK:
3299 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3300 res = gst_play_sink_send_event_to_sink (playsink, event);
3302 case GST_EVENT_STEP:
3307 gboolean flush, intermediate;
3309 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3312 if (format == GST_FORMAT_BUFFERS) {
3313 /* for buffers, we will try to step video frames, for other formats we
3314 * send the step to all sinks */
3315 res = gst_play_sink_send_event_to_sink (playsink, event);
3318 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3325 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3332 static GstStateChangeReturn
3333 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3335 GstStateChangeReturn ret;
3336 GstStateChangeReturn bret;
3338 GstPlaySink *playsink;
3340 playsink = GST_PLAY_SINK (element);
3342 switch (transition) {
3343 case GST_STATE_CHANGE_READY_TO_PAUSED:
3344 playsink->need_async_start = TRUE;
3345 /* we want to go async to PAUSED until we managed to configure and add the
3347 do_async_start (playsink);
3348 ret = GST_STATE_CHANGE_ASYNC;
3350 case GST_STATE_CHANGE_PAUSED_TO_READY:
3351 /* unblock all pads here */
3352 GST_PLAY_SINK_LOCK (playsink);
3353 if (playsink->video_pad) {
3355 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3356 (playsink->video_pad)));
3357 if (gst_pad_is_blocked (opad)) {
3358 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3359 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3361 gst_object_unref (opad);
3362 playsink->video_pad_blocked = FALSE;
3365 if (playsink->audio_pad) {
3367 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3368 (playsink->audio_pad)));
3370 if (gst_pad_is_blocked (opad)) {
3371 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3372 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3374 gst_object_unref (opad);
3375 playsink->audio_pad_blocked = FALSE;
3378 if (playsink->text_pad) {
3380 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3381 (playsink->text_pad)));
3382 if (gst_pad_is_blocked (opad)) {
3383 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3384 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3386 gst_object_unref (opad);
3387 playsink->text_pad_blocked = FALSE;
3389 GST_PLAY_SINK_UNLOCK (playsink);
3391 case GST_STATE_CHANGE_READY_TO_NULL:
3392 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3393 /* remove our links to the mute and volume elements when they were
3394 * provided by a sink */
3395 disconnect_chain (playsink->audiochain, playsink);
3396 playsink->audiochain->volume = NULL;
3397 playsink->audiochain->mute = NULL;
3398 playsink->audiochain->ts_offset = NULL;
3400 ret = GST_STATE_CHANGE_SUCCESS;
3403 /* all other state changes return SUCCESS by default, this value can be
3404 * overridden by the result of the children */
3405 ret = GST_STATE_CHANGE_SUCCESS;
3409 /* do the state change of the children */
3411 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3413 /* now look at the result of our children and adjust the return value */
3415 case GST_STATE_CHANGE_FAILURE:
3416 /* failure, we stop */
3417 goto activate_failed;
3418 case GST_STATE_CHANGE_NO_PREROLL:
3419 /* some child returned NO_PREROLL. This is strange but we never know. We
3420 * commit our async state change (if any) and return the NO_PREROLL */
3421 do_async_done (playsink);
3424 case GST_STATE_CHANGE_ASYNC:
3425 /* some child was async, return this */
3429 /* return our previously configured return value */
3433 switch (transition) {
3434 case GST_STATE_CHANGE_READY_TO_PAUSED:
3436 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3437 /* FIXME Release audio device when we implement that */
3438 playsink->need_async_start = TRUE;
3440 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3441 if (playsink->video_sinkpad_stream_synchronizer) {
3442 gst_element_release_request_pad (GST_ELEMENT_CAST
3443 (playsink->stream_synchronizer),
3444 playsink->video_sinkpad_stream_synchronizer);
3445 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3446 playsink->video_sinkpad_stream_synchronizer = NULL;
3447 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3448 playsink->video_srcpad_stream_synchronizer = NULL;
3450 if (playsink->audio_sinkpad_stream_synchronizer) {
3451 gst_element_release_request_pad (GST_ELEMENT_CAST
3452 (playsink->stream_synchronizer),
3453 playsink->audio_sinkpad_stream_synchronizer);
3454 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3455 playsink->audio_sinkpad_stream_synchronizer = NULL;
3456 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3457 playsink->audio_srcpad_stream_synchronizer = NULL;
3459 if (playsink->text_sinkpad_stream_synchronizer) {
3460 gst_element_release_request_pad (GST_ELEMENT_CAST
3461 (playsink->stream_synchronizer),
3462 playsink->text_sinkpad_stream_synchronizer);
3463 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3464 playsink->text_sinkpad_stream_synchronizer = NULL;
3465 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3466 playsink->text_srcpad_stream_synchronizer = NULL;
3470 case GST_STATE_CHANGE_READY_TO_NULL:
3471 /* remove sinks we added */
3472 if (playsink->videodeinterlacechain) {
3473 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3475 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3477 if (playsink->videochain) {
3478 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3479 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3481 if (playsink->audiochain) {
3482 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3483 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3485 if (playsink->vischain) {
3486 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3487 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3489 if (playsink->textchain) {
3490 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3491 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3493 do_async_done (playsink);
3494 /* when going to READY, keep elements around as long as possible,
3495 * so they may be re-used faster next time/url around.
3496 * when really going to NULL, clean up everything completely. */
3497 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3499 /* Unparent the sinks to allow reuse */
3500 if (playsink->videochain && playsink->videochain->sink)
3501 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3502 playsink->videochain->sink);
3503 if (playsink->audiochain && playsink->audiochain->sink)
3504 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3505 playsink->audiochain->sink);
3506 if (playsink->textchain && playsink->textchain->sink)
3507 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3508 playsink->textchain->sink);
3510 if (playsink->audio_sink != NULL)
3511 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3512 if (playsink->video_sink != NULL)
3513 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3514 if (playsink->visualisation != NULL)
3515 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3516 if (playsink->text_sink != NULL)
3517 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3519 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3520 playsink->videodeinterlacechain = NULL;
3521 free_chain ((GstPlayChain *) playsink->videochain);
3522 playsink->videochain = NULL;
3523 free_chain ((GstPlayChain *) playsink->audiochain);
3524 playsink->audiochain = NULL;
3525 free_chain ((GstPlayChain *) playsink->vischain);
3526 playsink->vischain = NULL;
3527 free_chain ((GstPlayChain *) playsink->textchain);
3528 playsink->textchain = NULL;
3539 GST_DEBUG_OBJECT (element,
3540 "element failed to change states -- activation problem?");
3541 return GST_STATE_CHANGE_FAILURE;
3546 gst_play_sink_set_property (GObject * object, guint prop_id,
3547 const GValue * value, GParamSpec * spec)
3549 GstPlaySink *playsink = GST_PLAY_SINK (object);
3553 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3556 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3559 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3561 case PROP_FONT_DESC:
3562 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3564 case PROP_SUBTITLE_ENCODING:
3565 gst_play_sink_set_subtitle_encoding (playsink,
3566 g_value_get_string (value));
3568 case PROP_VIS_PLUGIN:
3569 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3571 case PROP_AV_OFFSET:
3572 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3575 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3581 gst_play_sink_get_property (GObject * object, guint prop_id,
3582 GValue * value, GParamSpec * spec)
3584 GstPlaySink *playsink = GST_PLAY_SINK (object);
3588 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3591 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3594 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3596 case PROP_FONT_DESC:
3597 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3599 case PROP_SUBTITLE_ENCODING:
3600 g_value_take_string (value,
3601 gst_play_sink_get_subtitle_encoding (playsink));
3603 case PROP_VIS_PLUGIN:
3604 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3607 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3609 case PROP_AV_OFFSET:
3610 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3613 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3620 gst_play_sink_plugin_init (GstPlugin * plugin)
3622 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3624 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3625 GST_TYPE_PLAY_SINK);