2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
27 #include <gst/gst-i18n-plugin.h>
28 #include <gst/pbutils/pbutils.h>
30 #include "gstplaysink.h"
32 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
33 #define GST_CAT_DEFAULT gst_play_sink_debug
35 #define VOLUME_MAX_DOUBLE 10.0
37 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
39 /* holds the common data fields for the audio and video pipelines. We keep them
40 * in a structure to more easily have all the info available. */
43 GstPlaySink *playsink;
57 GstElement *volume; /* element with the volume property */
58 gboolean sink_volume; /* if the volume was provided by the sink */
59 GstElement *mute; /* element with the mute property */
81 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
82 GstPad *vissinkpad; /* visualisation sinkpad, */
84 GstPad *vissrcpad; /* visualisation srcpad, */
85 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
97 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
99 GstElement *sink; /* custom sink to receive subtitle buffers */
109 GstPad *videosinkpad;
111 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
113 GstElement *sink; /* custom sink to receive subpicture buffers */
116 #define GST_PLAY_SINK_GET_LOCK(playsink) (((GstPlaySink *)playsink)->lock)
117 #define GST_PLAY_SINK_LOCK(playsink) g_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink))
118 #define GST_PLAY_SINK_UNLOCK(playsink) g_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink))
126 gboolean async_pending;
127 gboolean need_async_start;
132 GstPlayAudioChain *audiochain;
133 GstPlayVideoChain *videochain;
134 GstPlayVisChain *vischain;
135 GstPlayTextChain *textchain;
136 GstPlaySubpChain *subpchain;
140 gboolean audio_pad_raw;
142 GstElement *audio_tee;
143 GstPad *audio_tee_sink;
144 GstPad *audio_tee_asrc;
145 GstPad *audio_tee_vissrc;
148 gboolean video_pad_raw;
155 GstElement *audio_sink;
156 GstElement *video_sink;
157 GstElement *visualisation;
158 GstElement *text_sink;
159 GstElement *subp_sink;
162 gchar *font_desc; /* font description */
163 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
165 gboolean volume_changed; /* volume/mute changed while no audiochain */
166 gboolean mute_changed; /* ... has been reated yet */
169 struct _GstPlaySinkClass
171 GstBinClass parent_class;
188 static void gst_play_sink_class_init (GstPlaySinkClass * klass);
189 static void gst_play_sink_init (GstPlaySink * playsink);
190 static void gst_play_sink_dispose (GObject * object);
191 static void gst_play_sink_finalize (GObject * object);
193 static gboolean gst_play_sink_send_event (GstElement * element,
195 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
196 GstStateChange transition);
198 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
200 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
202 static const GstElementDetails gst_play_sink_details =
203 GST_ELEMENT_DETAILS ("Player Sink",
204 "Generic/Bin/Player",
205 "Autoplug and play media from an uri",
206 "Wim Taymans <wim.taymans@gmail.com>");
208 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
211 gst_play_sink_class_init (GstPlaySinkClass * klass)
213 GObjectClass *gobject_klass;
214 GstElementClass *gstelement_klass;
215 GstBinClass *gstbin_klass;
217 gobject_klass = (GObjectClass *) klass;
218 gstelement_klass = (GstElementClass *) klass;
219 gstbin_klass = (GstBinClass *) klass;
221 gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_sink_dispose);
222 gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_sink_finalize);
224 gst_element_class_set_details (gstelement_klass, &gst_play_sink_details);
226 gstelement_klass->change_state =
227 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
228 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
230 gstbin_klass->handle_message =
231 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
233 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
237 gst_play_sink_init (GstPlaySink * playsink)
240 playsink->video_sink = NULL;
241 playsink->audio_sink = NULL;
242 playsink->visualisation = NULL;
243 playsink->text_sink = NULL;
244 playsink->volume = 1.0;
245 playsink->font_desc = NULL;
246 playsink->flags = GST_PLAY_FLAG_SOFT_VOLUME;
248 playsink->lock = g_mutex_new ();
249 playsink->need_async_start = TRUE;
250 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
254 free_chain (GstPlayChain * chain)
258 gst_object_unref (chain->bin);
264 gst_play_sink_dispose (GObject * object)
266 GstPlaySink *playsink;
268 playsink = GST_PLAY_SINK (object);
270 if (playsink->audio_sink != NULL) {
271 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
272 gst_object_unref (playsink->audio_sink);
273 playsink->audio_sink = NULL;
275 if (playsink->video_sink != NULL) {
276 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
277 gst_object_unref (playsink->video_sink);
278 playsink->video_sink = NULL;
280 if (playsink->visualisation != NULL) {
281 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
282 gst_object_unref (playsink->visualisation);
283 playsink->visualisation = NULL;
285 if (playsink->text_sink != NULL) {
286 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
287 gst_object_unref (playsink->text_sink);
288 playsink->text_sink = NULL;
291 free_chain ((GstPlayChain *) playsink->videochain);
292 playsink->videochain = NULL;
293 free_chain ((GstPlayChain *) playsink->audiochain);
294 playsink->audiochain = NULL;
295 free_chain ((GstPlayChain *) playsink->vischain);
296 playsink->vischain = NULL;
297 free_chain ((GstPlayChain *) playsink->textchain);
298 playsink->textchain = NULL;
300 if (playsink->audio_tee_sink) {
301 gst_object_unref (playsink->audio_tee_sink);
302 playsink->audio_tee_sink = NULL;
305 if (playsink->audio_tee_vissrc) {
306 gst_element_release_request_pad (playsink->audio_tee,
307 playsink->audio_tee_vissrc);
308 gst_object_unref (playsink->audio_tee_vissrc);
309 playsink->audio_tee_vissrc = NULL;
312 if (playsink->audio_tee_asrc) {
313 gst_element_release_request_pad (playsink->audio_tee,
314 playsink->audio_tee_asrc);
315 gst_object_unref (playsink->audio_tee_asrc);
316 playsink->audio_tee_asrc = NULL;
319 g_free (playsink->font_desc);
320 playsink->font_desc = NULL;
322 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
326 gst_play_sink_finalize (GObject * object)
328 GstPlaySink *playsink;
330 playsink = GST_PLAY_SINK (object);
332 g_mutex_free (playsink->lock);
334 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
338 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
341 GstElement **elem = NULL, *old = NULL;
343 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
345 GST_PLAY_SINK_LOCK (playsink);
347 case GST_PLAY_SINK_TYPE_AUDIO:
348 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
349 elem = &playsink->audio_sink;
351 case GST_PLAY_SINK_TYPE_VIDEO:
352 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
353 elem = &playsink->video_sink;
355 case GST_PLAY_SINK_TYPE_TEXT:
356 elem = &playsink->text_sink;
358 case GST_PLAY_SINK_TYPE_SUBPIC:
359 elem = &playsink->subp_sink;
367 gst_object_ref (sink);
370 GST_PLAY_SINK_UNLOCK (playsink);
373 gst_object_unref (old);
377 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
379 GstElement *result = NULL;
380 GstElement *elem = NULL, *chainp = NULL;
382 GST_PLAY_SINK_LOCK (playsink);
384 case GST_PLAY_SINK_TYPE_AUDIO:
386 GstPlayAudioChain *chain;
387 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
388 chainp = chain->sink;
389 elem = playsink->audio_sink;
392 case GST_PLAY_SINK_TYPE_VIDEO:
394 GstPlayVideoChain *chain;
395 if ((chain = (GstPlayVideoChain *) playsink->videochain))
396 chainp = chain->sink;
397 elem = playsink->video_sink;
400 case GST_PLAY_SINK_TYPE_TEXT:
402 GstPlayTextChain *chain;
403 if ((chain = (GstPlayTextChain *) playsink->textchain))
404 chainp = chain->sink;
405 elem = playsink->text_sink;
408 case GST_PLAY_SINK_TYPE_SUBPIC:
410 GstPlaySubpChain *chain;
411 if ((chain = (GstPlaySubpChain *) playsink->subpchain))
412 chainp = chain->sink;
413 elem = playsink->subp_sink;
420 /* we have an active chain with a sink, get the sink */
421 result = gst_object_ref (chainp);
423 /* nothing found, return last configured sink */
424 if (result == NULL && elem)
425 result = gst_object_ref (elem);
426 GST_PLAY_SINK_UNLOCK (playsink);
432 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
435 GstPlaySink *playsink;
437 playsink = GST_PLAY_SINK (user_data);
438 /* nothing to do here, we need a dummy callback here to make the async call
440 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
444 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
447 GstPlaySink *playsink;
448 GstPlayVisChain *chain;
450 playsink = GST_PLAY_SINK (user_data);
452 GST_PLAY_SINK_LOCK (playsink);
453 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
454 /* now try to change the plugin in the running vis chain */
455 if (!(chain = (GstPlayVisChain *) playsink->vischain))
458 /* unlink the old plugin and unghost the pad */
459 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
460 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
462 /* set the old plugin to NULL and remove */
463 gst_element_set_state (chain->vis, GST_STATE_NULL);
464 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
466 /* add new plugin and set state to playing */
467 chain->vis = playsink->visualisation;
468 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
469 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
472 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
473 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
476 gst_pad_link (chain->blockpad, chain->vissinkpad);
477 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
481 /* Unblock the pad */
482 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
484 GST_PLAY_SINK_UNLOCK (playsink);
488 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
490 GstPlayVisChain *chain;
492 /* setting NULL means creating the default vis plugin */
494 vis = gst_element_factory_make ("goom", "vis");
496 /* simply return if we don't have a vis plugin here */
500 GST_PLAY_SINK_LOCK (playsink);
501 /* first store the new vis */
502 if (playsink->visualisation)
503 gst_object_unref (playsink->visualisation);
505 gst_object_ref (vis);
506 gst_object_sink (vis);
507 playsink->visualisation = vis;
509 /* now try to change the plugin in the running vis chain, if we have no chain,
510 * we don't bother, any future vis chain will be created with the new vis
512 if (!(chain = (GstPlayVisChain *) playsink->vischain))
515 /* block the pad, the next time the callback is called we can change the
516 * visualisation. It's possible that this never happens or that the pad was
517 * already blocked. If the callback never happens, we don't have new data so
518 * we don't need the new vis plugin. If the pad was already blocked, the
519 * function returns FALSE but the previous pad block will do the right thing
521 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
522 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
525 GST_PLAY_SINK_UNLOCK (playsink);
531 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
533 GstElement *result = NULL;
534 GstPlayVisChain *chain;
536 GST_PLAY_SINK_LOCK (playsink);
537 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
538 /* we have an active chain, get the sink */
540 result = gst_object_ref (chain->vis);
542 /* nothing found, return last configured sink */
543 if (result == NULL && playsink->visualisation)
544 result = gst_object_ref (playsink->visualisation);
545 GST_PLAY_SINK_UNLOCK (playsink);
551 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
553 GstPlayAudioChain *chain;
555 GST_PLAY_SINK_LOCK (playsink);
556 playsink->volume = volume;
557 chain = (GstPlayAudioChain *) playsink->audiochain;
558 if (chain && chain->volume) {
559 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
560 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
561 chain->mute, volume, playsink->mute);
562 /* if there is a mute element or we are not muted, set the volume */
563 if (chain->mute || !playsink->mute)
564 g_object_set (chain->volume, "volume", volume, NULL);
566 GST_LOG_OBJECT (playsink, "no volume element");
567 playsink->volume_changed = TRUE;
569 GST_PLAY_SINK_UNLOCK (playsink);
573 gst_play_sink_get_volume (GstPlaySink * playsink)
576 GstPlayAudioChain *chain;
578 GST_PLAY_SINK_LOCK (playsink);
579 chain = (GstPlayAudioChain *) playsink->audiochain;
580 result = playsink->volume;
581 if (chain && chain->volume) {
582 if (chain->mute || !playsink->mute) {
583 g_object_get (chain->volume, "volume", &result, NULL);
584 playsink->volume = result;
587 GST_PLAY_SINK_UNLOCK (playsink);
593 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
595 GstPlayAudioChain *chain;
597 GST_PLAY_SINK_LOCK (playsink);
598 playsink->mute = mute;
599 chain = (GstPlayAudioChain *) playsink->audiochain;
602 g_object_set (chain->mute, "mute", mute, NULL);
603 } else if (chain->volume) {
605 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
607 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
611 playsink->mute_changed = TRUE;
613 GST_PLAY_SINK_UNLOCK (playsink);
617 gst_play_sink_get_mute (GstPlaySink * playsink)
620 GstPlayAudioChain *chain;
622 GST_PLAY_SINK_LOCK (playsink);
623 chain = (GstPlayAudioChain *) playsink->audiochain;
624 if (chain && chain->mute) {
625 g_object_get (chain->mute, "mute", &result, NULL);
626 playsink->mute = result;
628 result = playsink->mute;
630 GST_PLAY_SINK_UNLOCK (playsink);
636 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
640 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
641 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
645 add_chain (GstPlayChain * chain, gboolean add)
647 if (chain->added == add)
651 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
653 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
661 activate_chain (GstPlayChain * chain, gboolean activate)
665 if (chain->activated == activate)
668 GST_OBJECT_LOCK (chain->playsink);
669 state = GST_STATE_TARGET (chain->playsink);
670 GST_OBJECT_UNLOCK (chain->playsink);
673 gst_element_set_state (chain->bin, state);
675 gst_element_set_state (chain->bin, GST_STATE_NULL);
677 chain->activated = activate;
683 find_property (GstElement * element, const gchar * name)
687 if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), name)) {
689 GST_DEBUG_OBJECT (element, "found %s property", name);
691 GST_DEBUG_OBJECT (element, "did not find %s property", name);
693 gst_object_unref (element);
698 /* find an object in the hierarchy with a property named @name */
700 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
703 GstElement *result = NULL;
706 if (GST_IS_BIN (obj)) {
707 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
708 result = gst_iterator_find_custom (it,
709 (GCompareFunc) find_property, (gpointer) name);
710 gst_iterator_free (it);
712 if (g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name)) {
714 gst_object_ref (obj);
721 find_property_sink (GstElement * element, const gchar * name)
726 GST_OBJECT_LOCK (element);
727 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
728 GST_OBJECT_UNLOCK (element);
731 g_object_class_find_property (G_OBJECT_GET_CLASS (element), name)) {
733 GST_DEBUG_OBJECT (element, "found %s property on sink", name);
735 GST_DEBUG_OBJECT (element, "did not find %s property", name);
737 gst_object_unref (element);
742 /* find a sink in the hierarchy with a property named @name. This function does
743 * not increase the refcount of the returned object and thus remains valid as
744 * long as the bin is valid. */
746 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
749 GstElement *result = NULL;
752 if (g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name)) {
754 } else if (GST_IS_BIN (obj)) {
755 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
756 result = gst_iterator_find_custom (it,
757 (GCompareFunc) find_property_sink, (gpointer) name);
758 gst_iterator_free (it);
759 /* we don't need the extra ref */
761 gst_object_unref (result);
767 do_async_start (GstPlaySink * playsink)
771 if (!playsink->need_async_start)
774 playsink->async_pending = TRUE;
776 GST_INFO_OBJECT (playsink, "Sending async_start message");
777 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
778 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
779 (playsink), message);
783 do_async_done (GstPlaySink * playsink)
787 if (playsink->async_pending) {
788 GST_INFO_OBJECT (playsink, "Sending async_done message");
789 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
790 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
791 (playsink), message);
793 playsink->async_pending = FALSE;
796 playsink->need_async_start = FALSE;
799 /* try to change the state of an element. This function returns the element when
800 * the state change could be performed. When this function returns NULL an error
801 * occured and the element is unreffed if @unref is TRUE. */
803 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
805 GstStateChangeReturn ret;
808 ret = gst_element_set_state (element, GST_STATE_READY);
809 if (ret == GST_STATE_CHANGE_FAILURE) {
810 GST_DEBUG_OBJECT (playsink, "failed state change..");
811 gst_element_set_state (element, GST_STATE_NULL);
813 gst_object_unref (element);
820 /* make the element (bin) that contains the elements needed to perform
823 * +------------------------------------------------------------+
825 * | +-------+ +----------+ +----------+ +---------+ |
826 * | | queue | |colorspace| |videoscale| |videosink| |
827 * | +-sink src-sink src-sink src-sink | |
828 * | | +-------+ +----------+ +----------+ +---------+ |
830 * +------------------------------------------------------------+
833 static GstPlayVideoChain *
834 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async,
837 GstPlayVideoChain *chain;
840 GstElement *head, *prev, *elem = NULL;
842 chain = g_new0 (GstPlayVideoChain, 1);
843 chain->chain.playsink = playsink;
844 chain->chain.raw = raw;
846 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
848 if (playsink->video_sink) {
849 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
850 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
852 /* only try fallback if no specific sink was chosen */
853 if (chain->sink == NULL) {
854 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
855 elem = gst_element_factory_make ("autovideosink", "videosink");
856 chain->sink = try_element (playsink, elem, TRUE);
858 if (chain->sink == NULL) {
859 /* if default sink from config.h is different then try it too */
860 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
861 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
862 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
863 chain->sink = try_element (playsink, elem, TRUE);
867 if (chain->sink == NULL)
870 /* if we can disable async behaviour of the sink, we can avoid adding a
871 * queue for the audio chain. */
872 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
874 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
875 async, GST_ELEMENT_NAME (elem));
876 g_object_set (elem, "async", async, NULL);
877 chain->async = async;
879 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
883 /* create a bin to hold objects, as we create them we add them to this bin so
884 * that when something goes wrong we only need to unref the bin */
885 chain->chain.bin = gst_bin_new ("vbin");
886 bin = GST_BIN_CAST (chain->chain.bin);
887 gst_object_ref (bin);
888 gst_object_sink (bin);
889 gst_bin_add (bin, chain->sink);
892 /* decouple decoder from sink, this improves playback quite a lot since the
893 * decoder can continue while the sink blocks for synchronisation. We don't
894 * need a lot of buffers as this consumes a lot of memory and we don't want
895 * too little because else we would be context switching too quickly. */
896 chain->queue = gst_element_factory_make ("queue", "vqueue");
897 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
898 "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL);
899 gst_bin_add (bin, chain->queue);
900 head = prev = chain->queue;
906 if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
907 GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
908 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
909 if (chain->conv == NULL) {
910 post_missing_element_message (playsink, "ffmpegcolorspace");
911 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
912 (_("Missing element '%s' - check your GStreamer installation."),
913 "ffmpegcolorspace"), ("video rendering might fail"));
915 gst_bin_add (bin, chain->conv);
917 if (!gst_element_link_pads (prev, "src", chain->conv, "sink"))
925 GST_DEBUG_OBJECT (playsink, "creating videoscale");
926 chain->scale = gst_element_factory_make ("videoscale", "vscale");
927 if (chain->scale == NULL) {
928 post_missing_element_message (playsink, "videoscale");
929 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
930 (_("Missing element '%s' - check your GStreamer installation."),
931 "videoscale"), ("possibly a liboil version mismatch?"));
933 gst_bin_add (bin, chain->scale);
935 if (!gst_element_link_pads (prev, "src", chain->scale, "sink"))
945 GST_DEBUG_OBJECT (playsink, "linking to sink");
946 if (!gst_element_link_pads (prev, "src", chain->sink, NULL))
950 pad = gst_element_get_static_pad (head, "sink");
951 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
952 gst_object_unref (pad);
954 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
962 post_missing_element_message (playsink, "autovideosink");
963 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
964 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
965 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
966 (_("Both autovideosink and %s elements are missing."),
967 DEFAULT_VIDEOSINK), (NULL));
969 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
970 (_("The autovideosink element is missing.")), (NULL));
973 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
974 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
975 (_("Both autovideosink and %s elements are not working."),
976 DEFAULT_VIDEOSINK), (NULL));
978 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
979 (_("The autovideosink element is not working.")), (NULL));
982 free_chain ((GstPlayChain *) chain);
987 GST_ELEMENT_ERROR (playsink, CORE, PAD,
988 (NULL), ("Failed to configure the video sink."));
989 free_chain ((GstPlayChain *) chain);
995 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async,
999 GstPlayVideoChain *chain;
1000 GstStateChangeReturn ret;
1002 chain = playsink->videochain;
1004 /* if the chain was active we don't do anything */
1005 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1008 if (chain->chain.raw != raw)
1011 /* try to set the sink element to READY again */
1012 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1013 if (ret == GST_STATE_CHANGE_FAILURE)
1016 /* if we can disable async behaviour of the sink, we can avoid adding a
1017 * queue for the audio chain. */
1018 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
1020 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1021 async, GST_ELEMENT_NAME (elem));
1022 g_object_set (elem, "async", async, NULL);
1023 chain->async = async;
1025 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1026 chain->async = TRUE;
1031 /* make an element for playback of video with subtitles embedded.
1033 * +----------------------------------------------+
1034 * | tbin +-------------+ |
1035 * | +-----+ | textoverlay | |
1036 * | | csp | +--video_sink | |
1037 * sink-------sink src+ +-text_sink src--+ |
1038 * | +-----+ | +-------------+ +-- src
1039 * text_sink-------------+ |
1040 * +----------------------------------------------+
1042 static GstPlayTextChain *
1043 gen_text_chain (GstPlaySink * playsink)
1045 GstPlayTextChain *chain;
1048 GstPad *videosinkpad, *textsinkpad, *srcpad;
1050 chain = g_new0 (GstPlayTextChain, 1);
1051 chain->chain.playsink = playsink;
1053 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1055 chain->chain.bin = gst_bin_new ("tbin");
1056 bin = GST_BIN_CAST (chain->chain.bin);
1057 gst_object_ref (bin);
1058 gst_object_sink (bin);
1060 videosinkpad = textsinkpad = srcpad = NULL;
1062 /* first try to hook the text pad to the custom sink */
1063 if (playsink->text_sink) {
1064 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1065 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1067 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
1069 /* make sure the sparse subtitles don't participate in the preroll */
1070 g_object_set (elem, "async", FALSE, NULL);
1071 /* we have a custom sink, this will be our textsinkpad */
1072 textsinkpad = gst_element_get_static_pad (chain->sink, "sink");
1074 /* we're all fine now and we can add the sink to the chain */
1075 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1076 gst_bin_add (bin, chain->sink);
1078 GST_WARNING_OBJECT (playsink,
1079 "can't find a sink pad on custom text sink");
1080 gst_object_unref (chain->sink);
1083 /* try to set sync to true but it's no biggie when we can't */
1085 gst_play_sink_find_property_sinks (playsink, chain->sink,
1087 g_object_set (elem, "sync", TRUE, NULL);
1089 GST_WARNING_OBJECT (playsink,
1090 "can't find async property in custom text sink");
1093 if (textsinkpad == NULL) {
1094 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1095 (_("Custom text sink element is not usable.")),
1096 ("fallback to default textoverlay"));
1100 if (textsinkpad == NULL) {
1101 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1102 /* no custom sink, try to setup the colorspace and textoverlay elements */
1103 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "tconv");
1104 if (chain->conv == NULL) {
1105 /* not really needed, it might work without colorspace */
1106 post_missing_element_message (playsink, "ffmpegcolorspace");
1107 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1108 (_("Missing element '%s' - check your GStreamer installation."),
1109 "ffmpegcolorspace"), ("subtitle rendering might fail"));
1111 gst_bin_add (bin, chain->conv);
1112 videosinkpad = gst_element_get_static_pad (chain->conv, "sink");
1116 chain->overlay = gst_element_factory_make ("textoverlay", "overlay");
1117 if (chain->overlay == NULL) {
1118 post_missing_element_message (playsink, "textoverlay");
1119 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1120 (_("Missing element '%s' - check your GStreamer installation."),
1121 "textoverlay"), ("subtitle rendering disabled"));
1123 gst_bin_add (bin, chain->overlay);
1125 /* Set some parameters */
1126 g_object_set (G_OBJECT (chain->overlay),
1127 "halign", "center", "valign", "bottom", NULL);
1128 if (playsink->font_desc) {
1129 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1130 playsink->font_desc, NULL);
1132 g_object_set (G_OBJECT (chain->overlay), "wait-text", FALSE, NULL);
1134 textsinkpad = gst_element_get_static_pad (chain->overlay, "text_sink");
1136 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1139 /* if we had a videosinkpad, we had a converter and we can link it, we
1140 * know that this will work */
1141 gst_element_link_pads (chain->conv, "src", chain->overlay,
1144 /* no videopad, expose our own video pad then */
1146 gst_element_get_static_pad (chain->overlay, "video_sink");
1151 if (videosinkpad == NULL) {
1152 /* if we still don't have a videosink, we don't have a converter nor an
1153 * overlay. the only thing we can do is insert an identity and ghost the src
1155 chain->conv = gst_element_factory_make ("identity", "tidentity");
1156 g_object_set (chain->conv, "signal-handoffs", FALSE, NULL);
1157 g_object_set (chain->conv, "silent", TRUE, NULL);
1158 gst_bin_add (bin, chain->conv);
1159 srcpad = gst_element_get_static_pad (chain->conv, "src");
1160 videosinkpad = gst_element_get_static_pad (chain->conv, "sink");
1162 /* we have a videosink but maybe not a srcpad because there was no
1164 if (srcpad == NULL) {
1165 /* ghost the source pad of the converter then */
1166 srcpad = gst_element_get_static_pad (chain->conv, "src");
1170 /* expose the ghostpads */
1172 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1173 gst_object_unref (videosinkpad);
1174 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1177 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1178 gst_object_unref (textsinkpad);
1179 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1182 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1183 gst_object_unref (srcpad);
1184 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1190 /* make an element for playback of video with subpictures embedded.
1192 * +--------------------------------------------------------+
1193 * | pbin +-------------+ |
1194 * | +-------+ +-----+ | dvdspu | |
1195 * | | queue | | csp | +---video | |
1196 * sink----sink src--sink src+ +-subpicture src--+ |
1197 * | +-------+ +-----+ | +-------------+ +-- src
1198 * subpicture----------------------+ |
1199 * +--------------------------------------------------------+
1201 static GstPlaySubpChain *
1202 gen_subp_chain (GstPlaySink * playsink)
1204 GstPlaySubpChain *chain;
1206 GstElement *elem, *head;
1207 GstPad *videosinkpad, *subpsinkpad, *srcpad;
1209 chain = g_new0 (GstPlaySubpChain, 1);
1210 chain->chain.playsink = playsink;
1212 GST_DEBUG_OBJECT (playsink, "making subpicture chain %p", chain);
1214 chain->chain.bin = gst_bin_new ("pbin");
1215 bin = GST_BIN_CAST (chain->chain.bin);
1216 gst_object_ref (bin);
1217 gst_object_sink (bin);
1219 subpsinkpad = srcpad = NULL;
1221 /* first try to hook the text pad to the custom sink */
1222 if (playsink->subp_sink) {
1223 GST_DEBUG_OBJECT (playsink, "trying configured subpsink");
1224 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1226 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
1228 /* make sure the sparse subtitles don't participate in the preroll */
1229 g_object_set (elem, "async", FALSE, NULL);
1230 /* we have a custom sink, this will be our subpsinkpad */
1231 subpsinkpad = gst_element_get_static_pad (chain->sink, "sink");
1233 /* we're all fine now and we can add the sink to the chain */
1234 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1235 gst_bin_add (bin, chain->sink);
1237 GST_WARNING_OBJECT (playsink,
1238 "can't find a sink pad on custom text sink");
1239 gst_object_unref (chain->sink);
1242 /* try to set sync to true but it's no biggie when we can't */
1244 gst_play_sink_find_property_sinks (playsink, chain->sink,
1246 g_object_set (elem, "sync", TRUE, NULL);
1248 GST_WARNING_OBJECT (playsink,
1249 "can't find async property in custom text sink");
1252 if (subpsinkpad == NULL) {
1253 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1254 (_("Custom text sink element is not usable.")),
1255 ("fallback to default dvdspu overlay"));
1259 /* make a little queue */
1260 chain->queue = gst_element_factory_make ("queue", "vqueue");
1261 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1262 "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL);
1263 gst_bin_add (bin, chain->queue);
1264 head = chain->queue;
1266 /* video goes into the queue */
1267 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1269 if (subpsinkpad == NULL) {
1270 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1271 /* no custom sink, try to setup the colorspace and textoverlay elements */
1272 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "tconv");
1273 if (chain->conv == NULL) {
1274 /* not really needed, it might work without colorspace */
1275 post_missing_element_message (playsink, "ffmpegcolorspace");
1276 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1277 (_("Missing element '%s' - check your GStreamer installation."),
1278 "ffmpegcolorspace"), ("subpicture rendering might fail"));
1280 gst_bin_add (bin, chain->conv);
1281 gst_element_link_pads (head, "src", chain->conv, "sink");
1286 chain->overlay = gst_element_factory_make ("dvdspu", "spuoverlay");
1287 if (chain->overlay == NULL) {
1288 post_missing_element_message (playsink, "dvdspu");
1289 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1290 (_("Missing element '%s' - check your GStreamer installation."),
1291 "dvdspu"), ("subpicture rendering disabled"));
1293 gst_bin_add (bin, chain->overlay);
1294 /* Set some parameters */
1295 subpsinkpad = gst_element_get_static_pad (chain->overlay, "subpicture");
1296 /* link to the next element */
1297 gst_element_link_pads (head, "src", chain->overlay, "video");
1298 head = chain->overlay;
1301 srcpad = gst_element_get_static_pad (head, "src");
1302 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1303 gst_object_unref (srcpad);
1304 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1306 /* expose the ghostpads */
1307 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1308 gst_object_unref (videosinkpad);
1309 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1312 chain->subpsinkpad = gst_ghost_pad_new ("subpicture", subpsinkpad);
1313 gst_object_unref (subpsinkpad);
1314 gst_element_add_pad (chain->chain.bin, chain->subpsinkpad);
1319 /* make the chain that contains the elements needed to perform
1322 * We add a tee as the first element so that we can link the visualisation chain
1323 * to it when requested.
1325 * +-------------------------------------------------------------+
1327 * | +---------+ +----------+ +---------+ +---------+ |
1328 * | |audioconv| |audioscale| | volume | |audiosink| |
1329 * | +-srck src-sink src-sink src-sink | |
1330 * | | +---------+ +----------+ +---------+ +---------+ |
1332 * +-------------------------------------------------------------+
1334 static GstPlayAudioChain *
1335 gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
1337 GstPlayAudioChain *chain;
1339 gboolean have_volume;
1341 GstElement *head, *prev, *elem = NULL;
1343 chain = g_new0 (GstPlayAudioChain, 1);
1344 chain->chain.playsink = playsink;
1345 chain->chain.raw = raw;
1347 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1349 if (playsink->audio_sink) {
1350 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1351 playsink->audio_sink);
1352 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1354 /* only try fallback if no specific sink was chosen */
1355 if (chain->sink == NULL) {
1356 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1357 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1358 chain->sink = try_element (playsink, elem, TRUE);
1360 if (chain->sink == NULL) {
1361 /* if default sink from config.h is different then try it too */
1362 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1363 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1364 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1365 chain->sink = try_element (playsink, elem, TRUE);
1369 if (chain->sink == NULL)
1372 chain->chain.bin = gst_bin_new ("abin");
1373 bin = GST_BIN_CAST (chain->chain.bin);
1374 gst_object_ref (bin);
1375 gst_object_sink (bin);
1376 gst_bin_add (bin, chain->sink);
1379 /* we have to add a queue when we need to decouple for the video sink in
1381 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1382 chain->queue = gst_element_factory_make ("queue", "aqueue");
1383 gst_bin_add (bin, chain->queue);
1384 prev = head = chain->queue;
1390 /* check if the sink, or something within the sink, has the volume property.
1391 * If it does we don't need to add a volume element. */
1392 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "volume");
1394 chain->volume = elem;
1396 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1398 chain->sink_volume = TRUE;
1399 /* if the sink also has a mute property we can use this as well. We'll only
1400 * use the mute property if there is a volume property. We can simulate the
1401 * mute with the volume otherwise. */
1403 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute");
1405 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1407 /* use the sink to control the volume and mute */
1408 if (playsink->volume_changed) {
1409 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1411 if (playsink->mute_changed) {
1413 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1416 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1420 /* no volume, we need to add a volume element when we can */
1421 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1422 have_volume = FALSE;
1423 chain->sink_volume = FALSE;
1426 if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
1427 GST_DEBUG_OBJECT (playsink, "creating audioconvert");
1428 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1429 if (chain->conv == NULL) {
1430 post_missing_element_message (playsink, "audioconvert");
1431 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1432 (_("Missing element '%s' - check your GStreamer installation."),
1433 "audioconvert"), ("possibly a liboil version mismatch?"));
1435 gst_bin_add (bin, chain->conv);
1437 if (!gst_element_link_pads (prev, "src", chain->conv, "sink"))
1445 GST_DEBUG_OBJECT (playsink, "creating audioresample");
1446 chain->resample = gst_element_factory_make ("audioresample", "aresample");
1447 if (chain->resample == NULL) {
1448 post_missing_element_message (playsink, "audioresample");
1449 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1450 (_("Missing element '%s' - check your GStreamer installation."),
1451 "audioresample"), ("possibly a liboil version mismatch?"));
1453 gst_bin_add (bin, chain->resample);
1455 if (!gst_element_link_pads (prev, "src", chain->resample, "sink"))
1458 head = chain->resample;
1460 prev = chain->resample;
1463 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1464 GST_DEBUG_OBJECT (playsink, "creating volume");
1465 chain->volume = gst_element_factory_make ("volume", "volume");
1466 if (chain->volume == NULL) {
1467 post_missing_element_message (playsink, "volume");
1468 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1469 (_("Missing element '%s' - check your GStreamer installation."),
1470 "volume"), ("possibly a liboil version mismatch?"));
1474 /* volume also has the mute property */
1475 chain->mute = chain->volume;
1477 /* configure with the latest volume and mute */
1478 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1480 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1481 gst_bin_add (bin, chain->volume);
1484 if (!gst_element_link_pads (prev, "src", chain->volume, "sink"))
1487 head = chain->volume;
1489 prev = chain->volume;
1495 /* we only have to link to the previous element if we have something in
1496 * front of the sink */
1497 GST_DEBUG_OBJECT (playsink, "linking to sink");
1498 if (!gst_element_link_pads (prev, "src", chain->sink, NULL))
1502 /* post a warning if we have no way to configure the volume */
1504 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1505 (_("No volume control found")), ("Volume/mute is not available"));
1508 /* and ghost the sinkpad of the headmost element */
1509 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1510 pad = gst_element_get_static_pad (head, "sink");
1511 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1512 gst_object_unref (pad);
1513 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1521 post_missing_element_message (playsink, "autoaudiosink");
1522 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1523 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1524 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1525 (_("Both autoaudiosink and %s elements are missing."),
1526 DEFAULT_AUDIOSINK), (NULL));
1528 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1529 (_("The autoaudiosink element is missing.")), (NULL));
1532 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1533 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1534 (_("Both autoaudiosink and %s elements are not working."),
1535 DEFAULT_AUDIOSINK), (NULL));
1537 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1538 (_("The autoaudiosink element is not working.")), (NULL));
1541 free_chain ((GstPlayChain *) chain);
1546 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1547 (NULL), ("Failed to configure the audio sink."));
1548 free_chain ((GstPlayChain *) chain);
1554 setup_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
1557 GstPlayAudioChain *chain;
1558 GstStateChangeReturn ret;
1560 chain = playsink->audiochain;
1562 /* if the chain was active we don't do anything */
1563 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1566 if (chain->chain.raw != raw)
1569 /* try to set the sink element to READY again */
1570 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1571 if (ret == GST_STATE_CHANGE_FAILURE)
1574 /* check if the sink, or something within the sink, has the volume property.
1575 * If it does we don't need to add a volume element. */
1576 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "volume");
1578 chain->volume = elem;
1580 if (playsink->volume_changed) {
1581 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1583 /* use the sink to control the volume */
1584 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1586 /* if the sink also has a mute property we can use this as well. We'll only
1587 * use the mute property if there is a volume property. We can simulate the
1588 * mute with the volume otherwise. */
1590 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute");
1592 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1595 /* no volume, we need to add a volume element when we can */
1596 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1598 GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control");
1599 chain->volume = NULL;
1602 /* both last and current chain are raw audio, there should be a volume
1603 * element already, unless the sink changed from one with a volume
1604 * property to one that hasn't got a volume property, in which case we
1605 * re-generate the chain */
1606 if (chain->volume == NULL) {
1607 GST_DEBUG_OBJECT (playsink, "no existing volume element to re-use");
1611 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
1618 * +-------------------------------------------------------------------+
1620 * | +----------+ +------------+ +----------+ +-------+ |
1621 * | | visqueue | | audioconv | | audiores | | vis | |
1622 * | +-sink src-sink + samp src-sink src-sink src-+ |
1623 * | | +----------+ +------------+ +----------+ +-------+ | |
1625 * +-------------------------------------------------------------------+
1628 static GstPlayVisChain *
1629 gen_vis_chain (GstPlaySink * playsink)
1631 GstPlayVisChain *chain;
1637 chain = g_new0 (GstPlayVisChain, 1);
1638 chain->chain.playsink = playsink;
1640 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
1642 chain->chain.bin = gst_bin_new ("visbin");
1643 bin = GST_BIN_CAST (chain->chain.bin);
1644 gst_object_ref (bin);
1645 gst_object_sink (bin);
1647 /* we're queuing raw audio here, we can remove this queue when we can disable
1648 * async behaviour in the video sink. */
1649 chain->queue = gst_element_factory_make ("queue", "visqueue");
1650 gst_bin_add (bin, chain->queue);
1652 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1653 if (chain->conv == NULL)
1654 goto no_audioconvert;
1655 gst_bin_add (bin, chain->conv);
1657 chain->resample = gst_element_factory_make ("audioresample", "aresample");
1658 if (chain->resample == NULL)
1659 goto no_audioresample;
1660 gst_bin_add (bin, chain->resample);
1662 /* this pad will be used for blocking the dataflow and switching the vis
1664 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
1666 if (playsink->visualisation) {
1667 GST_DEBUG_OBJECT (playsink, "trying configure vis");
1668 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
1670 if (chain->vis == NULL) {
1671 GST_DEBUG_OBJECT (playsink, "trying goom");
1672 elem = gst_element_factory_make ("goom", "vis");
1673 chain->vis = try_element (playsink, elem, TRUE);
1675 if (chain->vis == NULL)
1678 gst_bin_add (bin, chain->vis);
1680 res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink");
1681 res &= gst_element_link_pads (chain->conv, "src", chain->resample, "sink");
1682 res &= gst_element_link_pads (chain->resample, "src", chain->vis, "sink");
1686 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
1687 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
1689 pad = gst_element_get_static_pad (chain->queue, "sink");
1690 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1691 gst_object_unref (pad);
1692 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1694 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
1695 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1702 post_missing_element_message (playsink, "audioconvert");
1703 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1704 (_("Missing element '%s' - check your GStreamer installation."),
1705 "audioconvert"), ("possibly a liboil version mismatch?"));
1706 free_chain ((GstPlayChain *) chain);
1711 post_missing_element_message (playsink, "audioresample");
1712 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1713 (_("Missing element '%s' - check your GStreamer installation."),
1714 "audioresample"), (NULL));
1715 free_chain ((GstPlayChain *) chain);
1720 post_missing_element_message (playsink, "goom");
1721 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1722 (_("Missing element '%s' - check your GStreamer installation."),
1724 free_chain ((GstPlayChain *) chain);
1729 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1730 (NULL), ("Failed to configure the visualisation element."));
1731 free_chain ((GstPlayChain *) chain);
1736 /* this function is called when all the request pads are requested and when we
1737 * have to construct the final pipeline. Based on the flags we construct the
1738 * final output pipelines.
1741 gst_play_sink_reconfigure (GstPlaySink * playsink)
1744 gboolean need_audio, need_video, need_vis, need_text, need_subp;
1746 GST_DEBUG_OBJECT (playsink, "reconfiguring");
1748 /* assume we need nothing */
1749 need_audio = need_video = need_vis = need_text = need_subp = FALSE;
1751 GST_PLAY_SINK_LOCK (playsink);
1752 GST_OBJECT_LOCK (playsink);
1753 /* get flags, there are protected with the object lock */
1754 flags = playsink->flags;
1755 GST_OBJECT_UNLOCK (playsink);
1757 /* figure out which components we need */
1758 if (flags & GST_PLAY_FLAG_TEXT && (playsink->text_pad || playsink->subp_pad)) {
1759 /* we have a text_pad and we need text rendering, in this case we need a
1760 * video_pad to combine the video with the text */
1761 if (!playsink->video_pad)
1762 goto subs_but_no_video;
1764 /* we have subtitles and we are requested to show it, we also need to show
1765 * video in this case. */
1767 need_text = (playsink->text_pad != NULL);
1768 need_subp = (playsink->subp_pad != NULL);
1770 /* we can't handle both of them yet */
1771 if (need_text && need_subp)
1773 } else if (flags & GST_PLAY_FLAG_VIDEO && playsink->video_pad) {
1774 /* we have video and we are requested to show it */
1777 if (playsink->audio_pad) {
1778 if (flags & GST_PLAY_FLAG_AUDIO) {
1781 if (playsink->audio_pad_raw) {
1782 /* only can do vis with raw uncompressed audio */
1783 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
1784 /* also add video when we add visualisation */
1791 /* set up video pipeline */
1793 gboolean raw, async, queue;
1795 /* we need a raw sink when we do vis or when we have a raw pad */
1796 raw = need_vis ? TRUE : playsink->video_pad_raw;
1797 /* we try to set the sink async=FALSE when we need vis, this way we can
1798 * avoid a queue in the audio chain. */
1800 /* put a little queue in front of the video but only if we are not doing
1801 * subpictures because then we will add the queue in front of the subpicture
1802 * mixer to minimize latency. */
1803 queue = (need_subp == FALSE);
1805 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
1806 playsink->video_pad_raw);
1808 if (playsink->videochain) {
1809 /* try to reactivate the chain */
1810 if (!setup_video_chain (playsink, raw, async, queue)) {
1811 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1812 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1813 free_chain ((GstPlayChain *) playsink->videochain);
1814 playsink->videochain = NULL;
1818 if (!playsink->videochain) {
1819 playsink->videochain = gen_video_chain (playsink, raw, async, queue);
1821 if (playsink->videochain) {
1822 GST_DEBUG_OBJECT (playsink, "adding video chain");
1823 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
1824 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
1825 /* if we are not part of vis or subtitles, set the ghostpad target */
1826 if (!need_vis && !need_text && playsink->text_pad == NULL) {
1827 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
1828 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1829 playsink->videochain->sinkpad);
1833 GST_DEBUG_OBJECT (playsink, "no video needed");
1834 if (playsink->videochain) {
1835 GST_DEBUG_OBJECT (playsink, "removing video chain");
1836 if (playsink->vischain) {
1839 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
1841 /* also had visualisation, release the tee srcpad before we then
1842 * unlink the video from it */
1843 if (playsink->audio_tee_vissrc) {
1844 gst_element_release_request_pad (playsink->audio_tee,
1845 playsink->audio_tee_vissrc);
1846 gst_object_unref (playsink->audio_tee_vissrc);
1847 playsink->audio_tee_vissrc = NULL;
1850 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
1851 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
1853 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1854 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1856 if (playsink->video_pad)
1857 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1861 GST_DEBUG_OBJECT (playsink, "adding text");
1862 if (!playsink->textchain) {
1863 GST_DEBUG_OBJECT (playsink, "creating text chain");
1864 playsink->textchain = gen_text_chain (playsink);
1866 if (playsink->textchain) {
1867 GST_DEBUG_OBJECT (playsink, "adding text chain");
1868 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
1869 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
1870 playsink->textchain->textsinkpad);
1871 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1872 playsink->textchain->videosinkpad);
1873 gst_pad_link (playsink->textchain->srcpad, playsink->videochain->sinkpad);
1874 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
1875 if (playsink->textchain->overlay)
1876 g_object_set (playsink->textchain->overlay, "silent", FALSE, NULL);
1879 GST_DEBUG_OBJECT (playsink, "no text needed");
1880 /* we have no subtitles/text or we are requested to not show them */
1881 if (playsink->textchain) {
1882 if (playsink->text_pad == NULL) {
1883 /* no text pad, remove the chain entirely */
1884 GST_DEBUG_OBJECT (playsink, "removing text chain");
1885 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
1886 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
1888 /* we have a chain and a textpad, turn the subtitles off */
1889 GST_DEBUG_OBJECT (playsink, "turning off the text");
1890 if (playsink->textchain->overlay)
1891 g_object_set (playsink->textchain->overlay, "silent", TRUE, NULL);
1894 if (!need_video && playsink->video_pad)
1895 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1896 if (playsink->text_pad)
1897 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
1900 if (need_subp && playsink->videochain) {
1901 GST_DEBUG_OBJECT (playsink, "adding subpicture");
1902 if (!playsink->subpchain) {
1903 GST_DEBUG_OBJECT (playsink, "creating subpicture chain");
1904 playsink->subpchain = gen_subp_chain (playsink);
1906 if (playsink->subpchain) {
1907 GST_DEBUG_OBJECT (playsink, "adding subp chain");
1908 add_chain (GST_PLAY_CHAIN (playsink->subpchain), TRUE);
1909 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->subp_pad),
1910 playsink->subpchain->subpsinkpad);
1911 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1912 playsink->subpchain->videosinkpad);
1913 gst_pad_link (playsink->subpchain->srcpad, playsink->videochain->sinkpad);
1914 activate_chain (GST_PLAY_CHAIN (playsink->subpchain), TRUE);
1917 GST_DEBUG_OBJECT (playsink, "no subpicture needed");
1918 /* we have no subpicture or we are requested to not show them */
1919 if (playsink->subpchain) {
1920 if (playsink->subp_pad == NULL) {
1921 /* no subpicture pad, remove the chain entirely */
1922 GST_DEBUG_OBJECT (playsink, "removing subp chain");
1923 add_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE);
1924 activate_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE);
1926 /* we have a chain and a subpicture pad, turn the subtitles off */
1927 GST_DEBUG_OBJECT (playsink, "turning off the subp");
1930 if (!need_video && playsink->video_pad)
1931 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1932 if (playsink->subp_pad)
1933 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->subp_pad), NULL);
1937 gboolean raw, queue;
1939 GST_DEBUG_OBJECT (playsink, "adding audio");
1941 /* get a raw sink if we are asked for a raw pad */
1942 raw = playsink->audio_pad_raw;
1943 if (need_vis && playsink->videochain) {
1944 /* If we are dealing with visualisations, we need to add a queue to
1945 * decouple the audio from the video part. We only have to do this when
1946 * the video part is async=true */
1947 queue = ((GstPlayVideoChain *) playsink->videochain)->async;
1948 GST_DEBUG_OBJECT (playsink, "need audio queue for vis: %d", queue);
1950 /* no vis, we can avoid a queue */
1951 GST_DEBUG_OBJECT (playsink, "don't need audio queue");
1955 if (playsink->audiochain) {
1956 /* try to reactivate the chain */
1957 if (!setup_audio_chain (playsink, raw, queue)) {
1958 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
1959 if (playsink->audio_tee_asrc) {
1960 gst_element_release_request_pad (playsink->audio_tee,
1961 playsink->audio_tee_asrc);
1962 gst_object_unref (playsink->audio_tee_asrc);
1963 playsink->audio_tee_asrc = NULL;
1965 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1966 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1967 playsink->audiochain->volume = NULL;
1968 playsink->audiochain->mute = NULL;
1969 free_chain ((GstPlayChain *) playsink->audiochain);
1970 playsink->audiochain = NULL;
1971 playsink->volume_changed = playsink->mute_changed = FALSE;
1975 if (!playsink->audiochain) {
1976 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
1977 playsink->audiochain = gen_audio_chain (playsink, raw, queue);
1980 if (playsink->audiochain) {
1981 GST_DEBUG_OBJECT (playsink, "adding audio chain");
1982 if (playsink->audio_tee_asrc == NULL) {
1983 playsink->audio_tee_asrc =
1984 gst_element_get_request_pad (playsink->audio_tee, "src%d");
1986 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
1987 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
1988 gst_pad_link (playsink->audio_tee_asrc, playsink->audiochain->sinkpad);
1991 GST_DEBUG_OBJECT (playsink, "no audio needed");
1992 /* we have no audio or we are requested to not play audio */
1993 if (playsink->audiochain) {
1994 GST_DEBUG_OBJECT (playsink, "removing audio chain");
1995 /* release the audio pad */
1996 if (playsink->audio_tee_asrc) {
1997 gst_element_release_request_pad (playsink->audio_tee,
1998 playsink->audio_tee_asrc);
1999 gst_object_unref (playsink->audio_tee_asrc);
2000 playsink->audio_tee_asrc = NULL;
2002 if (playsink->audiochain->sink_volume) {
2003 playsink->audiochain->volume = NULL;
2004 playsink->audiochain->mute = NULL;
2006 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2007 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2014 if (!playsink->vischain)
2015 playsink->vischain = gen_vis_chain (playsink);
2017 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2019 if (playsink->vischain) {
2020 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2022 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2023 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2024 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2025 if (playsink->audio_tee_vissrc == NULL) {
2026 playsink->audio_tee_vissrc =
2027 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2029 gst_pad_link (playsink->audio_tee_vissrc, playsink->vischain->sinkpad);
2030 gst_pad_link (srcpad, playsink->videochain->sinkpad);
2031 gst_object_unref (srcpad);
2034 GST_DEBUG_OBJECT (playsink, "no vis needed");
2035 if (playsink->vischain) {
2036 if (playsink->audio_tee_vissrc) {
2037 gst_element_release_request_pad (playsink->audio_tee,
2038 playsink->audio_tee_vissrc);
2039 gst_object_unref (playsink->audio_tee_vissrc);
2040 playsink->audio_tee_vissrc = NULL;
2042 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2043 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2044 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2047 do_async_done (playsink);
2048 GST_PLAY_SINK_UNLOCK (playsink);
2055 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2056 (_("Can't play a text file without video.")),
2057 ("Have text pad but no video pad"));
2058 GST_PLAY_SINK_UNLOCK (playsink);
2063 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2064 (_("Can't play a text subtitles and subpictures.")),
2065 ("Have text pad and subpicture pad"));
2066 GST_PLAY_SINK_UNLOCK (playsink);
2072 * gst_play_sink_set_flags:
2073 * @playsink: a #GstPlaySink
2074 * @flags: #GstPlayFlags
2076 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2077 * when constructing the sink pipelins.
2079 * Returns: TRUE if the flags could be configured.
2082 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2084 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2086 GST_OBJECT_LOCK (playsink);
2087 playsink->flags = flags;
2088 GST_OBJECT_UNLOCK (playsink);
2094 * gst_play_sink_get_flags:
2095 * @playsink: a #GstPlaySink
2097 * Get the flags of @playsink. That flags control the behaviour of the sink when
2098 * it constructs the sink pipelines.
2100 * Returns: the currently configured #GstPlayFlags.
2103 gst_play_sink_get_flags (GstPlaySink * playsink)
2107 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2109 GST_OBJECT_LOCK (playsink);
2110 res = playsink->flags;
2111 GST_OBJECT_UNLOCK (playsink);
2117 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2119 GstPlayTextChain *chain;
2121 GST_PLAY_SINK_LOCK (playsink);
2122 chain = (GstPlayTextChain *) playsink->textchain;
2123 g_free (playsink->font_desc);
2124 playsink->font_desc = g_strdup (desc);
2125 if (chain && chain->overlay) {
2126 g_object_set (chain->overlay, "font-desc", desc, NULL);
2128 GST_PLAY_SINK_UNLOCK (playsink);
2132 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2134 gchar *result = NULL;
2135 GstPlayTextChain *chain;
2137 GST_PLAY_SINK_LOCK (playsink);
2138 chain = (GstPlayTextChain *) playsink->textchain;
2139 if (chain && chain->overlay) {
2140 g_object_get (chain->overlay, "font-desc", &result, NULL);
2141 playsink->font_desc = g_strdup (result);
2143 result = g_strdup (playsink->font_desc);
2145 GST_PLAY_SINK_UNLOCK (playsink);
2151 * gst_play_sink_get_last_frame:
2152 * @playsink: a #GstPlaySink
2154 * Get the last displayed frame from @playsink. This frame is in the native
2155 * format of the sink element, the caps on the result buffer contain the format
2156 * of the frame data.
2158 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2162 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2164 GstBuffer *result = NULL;
2165 GstPlayVideoChain *chain;
2167 GST_PLAY_SINK_LOCK (playsink);
2168 GST_DEBUG_OBJECT (playsink, "taking last frame");
2169 /* get the video chain if we can */
2170 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2171 GST_DEBUG_OBJECT (playsink, "found video chain");
2172 /* see if the chain is active */
2173 if (chain->chain.activated && chain->sink) {
2176 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2178 /* find and get the last-buffer property now */
2180 gst_play_sink_find_property (playsink, chain->sink,
2182 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2183 g_object_get (elem, "last-buffer", &result, NULL);
2184 gst_object_unref (elem);
2188 GST_PLAY_SINK_UNLOCK (playsink);
2194 * gst_play_sink_request_pad
2195 * @playsink: a #GstPlaySink
2196 * @type: a #GstPlaySinkType
2198 * Create or return a pad of @type.
2200 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
2203 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
2206 gboolean created = FALSE;
2207 gboolean raw = FALSE;
2208 gboolean activate = TRUE;
2210 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
2212 GST_PLAY_SINK_LOCK (playsink);
2214 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2216 case GST_PLAY_SINK_TYPE_AUDIO:
2217 if (!playsink->audio_tee) {
2218 GST_LOG_OBJECT (playsink, "creating tee");
2219 /* create tee when needed. This element will feed the audio sink chain
2220 * and the vis chain. */
2221 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
2222 playsink->audio_tee_sink =
2223 gst_element_get_static_pad (playsink->audio_tee, "sink");
2224 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
2225 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
2227 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
2229 if (!playsink->audio_pad) {
2230 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
2231 playsink->audio_pad =
2232 gst_ghost_pad_new ("audio_sink", playsink->audio_tee_sink);
2235 playsink->audio_pad_raw = raw;
2236 res = playsink->audio_pad;
2238 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2240 case GST_PLAY_SINK_TYPE_VIDEO:
2241 if (!playsink->video_pad) {
2242 GST_LOG_OBJECT (playsink, "ghosting videosink");
2243 playsink->video_pad =
2244 gst_ghost_pad_new_no_target ("video_sink", GST_PAD_SINK);
2247 playsink->video_pad_raw = raw;
2248 res = playsink->video_pad;
2250 case GST_PLAY_SINK_TYPE_TEXT:
2251 GST_LOG_OBJECT (playsink, "ghosting text");
2252 if (!playsink->text_pad) {
2253 playsink->text_pad =
2254 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
2257 res = playsink->text_pad;
2259 case GST_PLAY_SINK_TYPE_FLUSHING:
2263 /* we need a unique padname for the flushing pad. */
2264 padname = g_strdup_printf ("flushing_%d", playsink->count);
2265 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
2272 case GST_PLAY_SINK_TYPE_SUBPIC:
2273 GST_LOG_OBJECT (playsink, "ghosting subpicture pad");
2274 if (!playsink->subp_pad) {
2275 playsink->subp_pad =
2276 gst_ghost_pad_new_no_target ("subp_sink", GST_PAD_SINK);
2279 res = playsink->subp_pad;
2285 GST_PLAY_SINK_UNLOCK (playsink);
2287 if (created && res) {
2288 /* we have to add the pad when it's active or we get an error when the
2289 * element is 'running' */
2290 gst_pad_set_active (res, TRUE);
2291 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
2293 gst_pad_set_active (res, activate);
2300 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
2302 GstPad **res = NULL;
2303 gboolean untarget = TRUE;
2305 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
2307 GST_PLAY_SINK_LOCK (playsink);
2308 if (pad == playsink->video_pad) {
2309 res = &playsink->video_pad;
2310 } else if (pad == playsink->audio_pad) {
2311 res = &playsink->audio_pad;
2312 } else if (pad == playsink->text_pad) {
2313 res = &playsink->text_pad;
2314 } else if (pad == playsink->subp_pad) {
2315 res = &playsink->subp_pad;
2317 /* try to release the given pad anyway, these could be the FLUSHING pads. */
2321 GST_PLAY_SINK_UNLOCK (playsink);
2324 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
2325 gst_pad_set_active (*res, FALSE);
2327 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
2328 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
2330 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
2331 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
2337 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
2339 GstPlaySink *playsink;
2341 playsink = GST_PLAY_SINK_CAST (bin);
2343 switch (GST_MESSAGE_TYPE (message)) {
2344 case GST_MESSAGE_STEP_DONE:
2349 gboolean flush, intermediate, eos;
2352 GST_INFO_OBJECT (playsink, "Handling step-done message");
2353 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
2354 &intermediate, &duration, &eos);
2356 if (format == GST_FORMAT_BUFFERS) {
2357 /* for the buffer format, we align the other streams */
2358 if (playsink->audiochain) {
2362 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
2365 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
2366 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
2370 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
2374 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
2379 /* Send an event to our sinks until one of them works; don't then send to the
2380 * remaining sinks (unlike GstBin)
2383 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
2385 gboolean res = TRUE;
2387 if (playsink->videochain) {
2388 gst_event_ref (event);
2389 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
2390 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
2393 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
2395 if (playsink->audiochain) {
2396 gst_event_ref (event);
2397 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
2398 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
2401 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
2404 gst_event_unref (event);
2408 /* We only want to send the event to a single sink (overriding GstBin's
2409 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
2410 * events appropriately. So, this is a messy duplication of code. */
2412 gst_play_sink_send_event (GstElement * element, GstEvent * event)
2414 gboolean res = FALSE;
2415 GstEventType event_type = GST_EVENT_TYPE (event);
2416 GstPlaySink *playsink;
2418 playsink = GST_PLAY_SINK_CAST (element);
2420 switch (event_type) {
2421 case GST_EVENT_SEEK:
2422 GST_DEBUG_OBJECT (element, "Sending event to a sink");
2423 res = gst_play_sink_send_event_to_sink (playsink, event);
2425 case GST_EVENT_STEP:
2430 gboolean flush, intermediate;
2432 gst_event_parse_step (event, &format, &amount, &rate, &flush,
2435 if (format == GST_FORMAT_BUFFERS) {
2436 /* for buffers, we will try to step video frames, for other formats we
2437 * send the step to all sinks */
2438 res = gst_play_sink_send_event_to_sink (playsink, event);
2441 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
2448 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
2455 static GstStateChangeReturn
2456 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
2458 GstStateChangeReturn ret;
2459 GstStateChangeReturn bret;
2461 GstPlaySink *playsink;
2463 playsink = GST_PLAY_SINK (element);
2465 switch (transition) {
2466 case GST_STATE_CHANGE_READY_TO_PAUSED:
2467 /* we want to go async to PAUSED until we managed to configure and add the
2469 do_async_start (playsink);
2470 ret = GST_STATE_CHANGE_ASYNC;
2472 case GST_STATE_CHANGE_PAUSED_TO_READY:
2473 case GST_STATE_CHANGE_READY_TO_NULL:
2474 if (playsink->audiochain && playsink->audiochain->sink_volume) {
2475 /* remove our links to the mute and volume elements when they were
2476 * provided by a sink */
2477 playsink->audiochain->volume = NULL;
2478 playsink->audiochain->mute = NULL;
2480 ret = GST_STATE_CHANGE_SUCCESS;
2483 /* all other state changes return SUCCESS by default, this value can be
2484 * overridden by the result of the children */
2485 ret = GST_STATE_CHANGE_SUCCESS;
2489 /* do the state change of the children */
2491 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
2493 /* now look at the result of our children and adjust the return value */
2495 case GST_STATE_CHANGE_FAILURE:
2496 /* failure, we stop */
2497 goto activate_failed;
2498 case GST_STATE_CHANGE_NO_PREROLL:
2499 /* some child returned NO_PREROLL. This is strange but we never know. We
2500 * commit our async state change (if any) and return the NO_PREROLL */
2501 do_async_done (playsink);
2504 case GST_STATE_CHANGE_ASYNC:
2505 /* some child was async, return this */
2509 /* return our previously configured return value */
2513 switch (transition) {
2514 case GST_STATE_CHANGE_READY_TO_PAUSED:
2516 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2517 /* FIXME Release audio device when we implement that */
2518 playsink->need_async_start = TRUE;
2520 case GST_STATE_CHANGE_PAUSED_TO_READY:
2521 case GST_STATE_CHANGE_READY_TO_NULL:
2522 /* remove sinks we added */
2523 if (playsink->videochain) {
2524 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2525 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2527 if (playsink->audiochain) {
2528 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2529 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2531 if (playsink->vischain) {
2532 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2533 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2535 if (playsink->textchain) {
2536 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2537 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2539 if (playsink->subpchain) {
2540 activate_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE);
2541 add_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE);
2543 do_async_done (playsink);
2553 GST_DEBUG_OBJECT (element,
2554 "element failed to change states -- activation problem?");
2555 return GST_STATE_CHANGE_FAILURE;