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
36 #define CONNECTION_SPEED_DEFAULT 0
38 /* holds the common data fields for the audio and video pipelines. We keep them
39 * in a structure to more easily have all the info available. */
42 GstPlaySink *playsink;
80 #define GST_PLAY_SINK_GET_LOCK(playsink) (((GstPlaySink *)playsink)->lock)
81 #define GST_PLAY_SINK_LOCK(playsink) g_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink))
82 #define GST_PLAY_SINK_UNLOCK(playsink) g_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink))
92 GstPlayChain *audiochain;
93 GstPlayChain *videochain;
94 GstPlayChain *vischain;
97 gboolean audio_pad_raw;
99 gboolean video_pad_raw;
103 GstElement *audio_sink;
104 GstElement *video_sink;
105 GstElement *visualisation;
107 gchar *font_desc; /* font description */
108 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
110 /* internal elements */
111 GstElement *textoverlay_element;
113 GstElement *pending_visualisation;
114 GstElement *fakesink;
117 struct _GstPlaySinkClass
119 GstBinClass parent_class;
142 static void gst_play_sink_class_init (GstPlaySinkClass * klass);
143 static void gst_play_sink_init (GstPlaySink * playsink);
144 static void gst_play_sink_dispose (GObject * object);
145 static void gst_play_sink_finalize (GObject * object);
147 static void gst_play_sink_set_property (GObject * object, guint prop_id,
148 const GValue * value, GParamSpec * spec);
149 static void gst_play_sink_get_property (GObject * object, guint prop_id,
150 GValue * value, GParamSpec * spec);
152 static gboolean gst_play_sink_send_event (GstElement * element,
154 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
155 GstStateChange transition);
157 static GstElementClass *parent_class;
159 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
161 static const GstElementDetails gst_play_sink_details =
162 GST_ELEMENT_DETAILS ("Player Sink",
163 "Generic/Bin/Player",
164 "Autoplug and play media from an uri",
165 "Wim Taymans <wim.taymans@gmail.com>");
168 gst_play_sink_get_type (void)
170 static GType gst_play_sink_type = 0;
172 if (!gst_play_sink_type) {
173 static const GTypeInfo gst_play_sink_info = {
174 sizeof (GstPlaySinkClass),
177 (GClassInitFunc) gst_play_sink_class_init,
180 sizeof (GstPlaySink),
182 (GInstanceInitFunc) gst_play_sink_init,
186 gst_play_sink_type = g_type_register_static (GST_TYPE_BIN,
187 "GstPlaySink", &gst_play_sink_info, 0);
190 return gst_play_sink_type;
194 gst_play_sink_class_init (GstPlaySinkClass * klass)
196 GObjectClass *gobject_klass;
197 GstElementClass *gstelement_klass;
198 GstBinClass *gstbin_klass;
200 gobject_klass = (GObjectClass *) klass;
201 gstelement_klass = (GstElementClass *) klass;
202 gstbin_klass = (GstBinClass *) klass;
204 parent_class = g_type_class_peek_parent (klass);
206 gobject_klass->set_property = gst_play_sink_set_property;
207 gobject_klass->get_property = gst_play_sink_get_property;
209 gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_sink_dispose);
210 gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_sink_finalize);
212 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
213 g_param_spec_object ("video-sink", "Video Sink",
214 "the video output element to use (NULL = default sink)",
215 GST_TYPE_ELEMENT, G_PARAM_READWRITE));
216 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
217 g_param_spec_object ("audio-sink", "Audio Sink",
218 "the audio output element to use (NULL = default sink)",
219 GST_TYPE_ELEMENT, G_PARAM_READWRITE));
220 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
221 g_param_spec_object ("vis-plugin", "Vis plugin",
222 "the visualization element to use (NULL = none)",
223 GST_TYPE_ELEMENT, G_PARAM_READWRITE));
224 g_object_class_install_property (gobject_klass, PROP_VOLUME,
225 g_param_spec_double ("volume", "volume", "volume",
226 0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE));
227 g_object_class_install_property (gobject_klass, PROP_FRAME,
228 gst_param_spec_mini_object ("frame", "Frame",
229 "The last frame (NULL = no video available)",
230 GST_TYPE_BUFFER, G_PARAM_READABLE));
231 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
232 g_param_spec_string ("subtitle-font-desc",
233 "Subtitle font description",
234 "Pango font description of font "
235 "to be used for subtitle rendering", NULL, G_PARAM_WRITABLE));
237 gst_element_class_set_details (gstelement_klass, &gst_play_sink_details);
239 gstelement_klass->change_state =
240 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
241 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
243 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
247 gst_play_sink_init (GstPlaySink * playsink)
250 playsink->video_sink = NULL;
251 playsink->audio_sink = NULL;
252 playsink->visualisation = NULL;
253 playsink->pending_visualisation = NULL;
254 playsink->textoverlay_element = NULL;
255 playsink->volume = 1.0;
256 playsink->font_desc = NULL;
257 playsink->flags = GST_PLAY_FLAG_SOFT_VOLUME;
259 playsink->lock = g_mutex_new ();
263 gst_play_sink_dispose (GObject * object)
265 GstPlaySink *playsink;
267 playsink = GST_PLAY_SINK (object);
269 if (playsink->audio_sink != NULL) {
270 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
271 gst_object_unref (playsink->audio_sink);
272 playsink->audio_sink = NULL;
274 if (playsink->video_sink != NULL) {
275 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
276 gst_object_unref (playsink->video_sink);
277 playsink->video_sink = NULL;
279 if (playsink->visualisation != NULL) {
280 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
281 gst_object_unref (playsink->visualisation);
282 playsink->visualisation = NULL;
284 if (playsink->pending_visualisation != NULL) {
285 gst_element_set_state (playsink->pending_visualisation, GST_STATE_NULL);
286 gst_object_unref (playsink->pending_visualisation);
287 playsink->pending_visualisation = NULL;
289 if (playsink->textoverlay_element != NULL) {
290 gst_object_unref (playsink->textoverlay_element);
291 playsink->textoverlay_element = NULL;
293 g_free (playsink->font_desc);
294 playsink->font_desc = NULL;
296 G_OBJECT_CLASS (parent_class)->dispose (object);
300 gst_play_sink_finalize (GObject * object)
302 GstPlaySink *playsink;
304 playsink = GST_PLAY_SINK (object);
306 g_mutex_free (playsink->lock);
308 G_OBJECT_CLASS (parent_class)->finalize (object);
312 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
315 GstPlaySink *playsink = GST_PLAY_SINK (user_data);
317 if (playsink->pending_visualisation)
318 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
323 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
326 GstPlaySink *playsink = GST_PLAY_SINK (user_data);
327 GstBin *vis_bin = NULL;
328 GstPad *vis_sink_pad = NULL, *vis_src_pad = NULL, *vqueue_pad = NULL;
330 GstElement *pending_visualisation;
332 GST_OBJECT_LOCK (playsink);
333 pending_visualisation = playsink->pending_visualisation;
334 playsink->pending_visualisation = NULL;
335 GST_OBJECT_UNLOCK (playsink);
337 /* We want to disable visualisation */
338 if (!GST_IS_ELEMENT (pending_visualisation)) {
339 /* Set visualisation element to READY */
340 gst_element_set_state (playsink->visualisation, GST_STATE_READY);
345 GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST (playsink->
348 if (!GST_IS_BIN (vis_bin) || !GST_IS_PAD (tee_pad)) {
352 vis_src_pad = gst_element_get_pad (playsink->visualisation, "src");
353 vis_sink_pad = gst_pad_get_peer (tee_pad);
355 /* Can be fakesink */
356 if (GST_IS_PAD (vis_src_pad)) {
357 vqueue_pad = gst_pad_get_peer (vis_src_pad);
360 if (!GST_IS_PAD (vis_sink_pad)) {
364 /* Check the bin's state */
365 GST_OBJECT_LOCK (vis_bin);
366 bin_state = GST_STATE (vis_bin);
367 GST_OBJECT_UNLOCK (vis_bin);
370 gst_pad_unlink (tee_pad, vis_sink_pad);
371 gst_object_unref (vis_sink_pad);
374 if (GST_IS_PAD (vqueue_pad)) {
375 gst_pad_unlink (vis_src_pad, vqueue_pad);
376 gst_object_unref (vis_src_pad);
380 /* Remove from vis_bin */
381 gst_bin_remove (vis_bin, playsink->visualisation);
382 /* Set state to NULL */
383 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
384 /* And loose our ref */
385 gst_object_unref (playsink->visualisation);
387 if (pending_visualisation) {
388 /* Ref this new visualisation element before adding to the bin */
389 gst_object_ref (pending_visualisation);
390 /* Add the new one */
391 gst_bin_add (vis_bin, pending_visualisation);
392 /* Synchronizing state */
393 gst_element_set_state (pending_visualisation, bin_state);
395 vis_sink_pad = gst_element_get_pad (pending_visualisation, "sink");
396 vis_src_pad = gst_element_get_pad (pending_visualisation, "src");
398 if (!GST_IS_PAD (vis_sink_pad) || !GST_IS_PAD (vis_src_pad)) {
403 gst_pad_link (tee_pad, vis_sink_pad);
404 gst_pad_link (vis_src_pad, vqueue_pad);
408 gst_object_unref (playsink->visualisation);
409 playsink->visualisation = pending_visualisation;
413 gst_object_unref (vis_sink_pad);
416 gst_object_unref (vis_src_pad);
419 gst_object_unref (vqueue_pad);
422 gst_object_unref (vis_bin);
425 /* Unblock the pad */
426 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
431 gst_play_sink_set_video_sink (GstPlaySink * playsink, GstElement * sink)
433 GST_OBJECT_LOCK (playsink);
434 if (playsink->video_sink)
435 gst_object_unref (playsink->video_sink);
438 gst_object_ref (sink);
439 gst_object_sink (sink);
441 playsink->video_sink = sink;
442 GST_OBJECT_UNLOCK (playsink);
446 gst_play_sink_set_audio_sink (GstPlaySink * playsink, GstElement * sink)
448 GST_OBJECT_LOCK (playsink);
449 if (playsink->audio_sink)
450 gst_object_unref (playsink->audio_sink);
453 gst_object_ref (sink);
454 gst_object_sink (sink);
456 playsink->audio_sink = sink;
457 GST_OBJECT_UNLOCK (playsink);
461 gst_play_sink_set_vis_plugin (GstPlaySink * playsink,
462 GstElement * pending_visualisation)
465 if (pending_visualisation) {
466 gst_object_ref (pending_visualisation);
467 gst_object_sink (pending_visualisation);
470 /* Do we already have a visualisation change pending? If yes, change the
471 * pending vis with the new one. */
472 GST_OBJECT_LOCK (playsink);
473 if (playsink->pending_visualisation) {
474 gst_object_unref (playsink->pending_visualisation);
475 playsink->pending_visualisation = pending_visualisation;
476 GST_OBJECT_UNLOCK (playsink);
478 GST_OBJECT_UNLOCK (playsink);
479 /* Was there a visualisation already set ? */
480 if (playsink->visualisation != NULL) {
481 GstBin *vis_bin = NULL;
484 GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST (playsink->
487 /* Check if the visualisation is already in a bin */
488 if (GST_IS_BIN (vis_bin)) {
489 GstPad *vis_sink_pad = NULL, *tee_pad = NULL;
491 /* Now get tee pad and block it async */
492 vis_sink_pad = gst_element_get_pad (playsink->visualisation, "sink");
493 if (!GST_IS_PAD (vis_sink_pad)) {
496 tee_pad = gst_pad_get_peer (vis_sink_pad);
497 if (!GST_IS_PAD (tee_pad)) {
501 playsink->pending_visualisation = pending_visualisation;
502 /* Block with callback */
503 gst_pad_set_blocked_async (tee_pad, TRUE, gst_play_sink_vis_blocked,
507 gst_object_unref (vis_sink_pad);
510 gst_object_unref (tee_pad);
512 gst_object_unref (vis_bin);
514 playsink->visualisation = pending_visualisation;
517 playsink->visualisation = pending_visualisation;
523 gst_play_sink_set_property (GObject * object, guint prop_id,
524 const GValue * value, GParamSpec * pspec)
526 GstPlaySink *playsink;
528 playsink = GST_PLAY_SINK (object);
531 case PROP_VIDEO_SINK:
532 gst_play_sink_set_video_sink (playsink, g_value_get_object (value));
534 case PROP_AUDIO_SINK:
535 gst_play_sink_set_audio_sink (playsink, g_value_get_object (value));
537 case PROP_VIS_PLUGIN:
538 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
541 GST_OBJECT_LOCK (playsink);
542 playsink->volume = g_value_get_double (value);
543 GST_OBJECT_UNLOCK (playsink);
546 GST_OBJECT_LOCK (playsink);
547 g_free (playsink->font_desc);
548 playsink->font_desc = g_strdup (g_value_get_string (value));
549 if (playsink->textoverlay_element) {
550 g_object_set (G_OBJECT (playsink->textoverlay_element),
551 "font-desc", g_value_get_string (value), NULL);
553 GST_OBJECT_UNLOCK (playsink);
556 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
562 gst_play_sink_get_property (GObject * object, guint prop_id, GValue * value,
565 GstPlaySink *playsink;
567 playsink = GST_PLAY_SINK (object);
570 case PROP_VIDEO_SINK:
571 GST_OBJECT_LOCK (playsink);
572 g_value_set_object (value, playsink->video_sink);
573 GST_OBJECT_UNLOCK (playsink);
575 case PROP_AUDIO_SINK:
576 GST_OBJECT_LOCK (playsink);
577 g_value_set_object (value, playsink->audio_sink);
578 GST_OBJECT_UNLOCK (playsink);
580 case PROP_VIS_PLUGIN:
581 GST_OBJECT_LOCK (playsink);
582 g_value_set_object (value, playsink->visualisation);
583 GST_OBJECT_UNLOCK (playsink);
586 GST_OBJECT_LOCK (playsink);
587 g_value_set_double (value, playsink->volume);
588 GST_OBJECT_UNLOCK (playsink);
595 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
601 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
605 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
606 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
610 free_chain (GstPlayChain * chain)
613 gst_object_unref (chain->bin);
614 gst_object_unref (chain->playsink);
619 add_chain (GstPlayChain * chain, gboolean add)
621 if (chain->added == add)
625 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
627 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
635 activate_chain (GstPlayChain * chain, gboolean activate)
637 if (chain->activated == activate)
641 gst_element_set_state (chain->bin, GST_STATE_PAUSED);
643 gst_element_set_state (chain->bin, GST_STATE_NULL);
645 chain->activated = activate;
650 /* make the element (bin) that contains the elements needed to perform
653 * +------------------------------------------------+
655 * | +----------+ +----------+ +---------+ |
656 * | |colorspace| |videoscale| |videosink| |
657 * | +-sink src-sink src-sink | |
658 * | | +----------+ +----------+ +---------+ |
660 * +------------------------------------------------+
663 static GstPlayChain *
664 gen_video_chain (GstPlaySink * playsink, gboolean raw)
666 GstPlayVideoChain *chain;
670 chain = g_new0 (GstPlayVideoChain, 1);
671 chain->chain.playsink = gst_object_ref (playsink);
673 if (playsink->video_sink) {
674 chain->sink = playsink->video_sink;
676 chain->sink = gst_element_factory_make ("autovideosink", "videosink");
677 if (chain->sink == NULL) {
678 chain->sink = gst_element_factory_make ("xvimagesink", "videosink");
680 if (chain->sink == NULL)
684 /* create a bin to hold objects, as we create them we add them to this bin so
685 * that when something goes wrong we only need to unref the bin */
686 chain->chain.bin = gst_bin_new ("vbin");
687 bin = GST_BIN_CAST (chain->chain.bin);
688 gst_object_ref (bin);
689 gst_object_sink (bin);
690 gst_bin_add (bin, chain->sink);
693 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
694 if (chain->conv == NULL)
696 gst_bin_add (bin, chain->conv);
698 chain->scale = gst_element_factory_make ("videoscale", "vscale");
699 if (chain->scale == NULL)
701 gst_bin_add (bin, chain->scale);
704 /* decouple decoder from sink, this improves playback quite a lot since the
705 * decoder can continue while the sink blocks for synchronisation. We don't
706 * need a lot of buffers as this consumes a lot of memory and we don't want
707 * too little because else we would be context switching too quickly. */
708 chain->queue = gst_element_factory_make ("queue", "vqueue");
709 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
710 "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL);
711 gst_bin_add (bin, chain->queue);
714 gst_element_link_pads (chain->conv, "src", chain->queue, "sink");
715 gst_element_link_pads (chain->queue, "src", chain->scale, "sink");
716 /* be more careful with the pad from the custom sink element, it might not
718 if (!gst_element_link_pads (chain->scale, "src", chain->sink, NULL))
721 pad = gst_element_get_pad (chain->conv, "sink");
723 if (!gst_element_link_pads (chain->queue, "src", chain->sink, NULL))
725 pad = gst_element_get_pad (chain->queue, "sink");
728 chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad);
729 gst_object_unref (pad);
730 gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad);
732 return (GstPlayChain *) chain;
737 post_missing_element_message (playsink, "autovideosink");
738 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
739 (_("Both autovideosink and xvimagesink elements are missing.")),
741 free_chain ((GstPlayChain *) chain);
746 post_missing_element_message (playsink, "ffmpegcolorspace");
747 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
748 (_("Missing element '%s' - check your GStreamer installation."),
749 "ffmpegcolorspace"), (NULL));
750 free_chain ((GstPlayChain *) chain);
756 post_missing_element_message (playsink, "videoscale");
757 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
758 (_("Missing element '%s' - check your GStreamer installation."),
759 "videoscale"), ("possibly a liboil version mismatch?"));
760 free_chain ((GstPlayChain *) chain);
765 GST_ELEMENT_ERROR (playsink, CORE, PAD,
766 (NULL), ("Failed to configure the video sink."));
767 free_chain ((GstPlayChain *) chain);
773 /* make an element for playback of video with subtitles embedded.
775 * +--------------------------------------------------+
776 * | tbin +-------------+ |
777 * | +-----+ | textoverlay | +------+ |
778 * | | csp | +--video_sink | | vbin | |
779 * video_sink-sink src+ +-text_sink src-sink | |
780 * | +-----+ | +-------------+ +------+ |
781 * text_sink-------------+ |
782 * +--------------------------------------------------+
784 * If there is no subtitle renderer this function will simply return the
785 * videosink without the text_sink pad.
788 gen_text_element (GstPlaySink * playsink)
790 GstElement *element, *csp, *overlay, *vbin;
793 /* Create the video rendering bin, error is posted when this fails. */
794 vbin = gen_video_element (playsink);
799 overlay = gst_element_factory_make ("textoverlay", "overlay");
801 /* If no overlay return the video bin without subtitle support. */
806 element = gst_bin_new ("textbin");
808 /* Set some parameters */
809 g_object_set (G_OBJECT (overlay),
810 "halign", "center", "valign", "bottom", NULL);
811 if (playsink->font_desc) {
812 g_object_set (G_OBJECT (overlay), "font-desc", playsink->font_desc, NULL);
816 playsink->textoverlay_element = GST_ELEMENT_CAST (gst_object_ref (overlay));
818 /* we know this will succeed, as the video bin already created one before */
819 csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
821 /* Add our elements */
822 gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL);
825 gst_element_link_pads (csp, "src", overlay, "video_sink");
826 gst_element_link_pads (overlay, "src", vbin, "sink");
828 /* Add ghost pads on the subtitle bin */
829 pad = gst_element_get_pad (overlay, "text_sink");
830 gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad));
831 gst_object_unref (pad);
833 pad = gst_element_get_pad (csp, "sink");
834 gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
835 gst_object_unref (pad);
837 /* Set state to READY */
838 gst_element_set_state (element, GST_STATE_READY);
845 post_missing_element_message (playsink, "textoverlay");
846 GST_WARNING_OBJECT (playsink,
847 "No overlay (pango) element, subtitles disabled");
854 /* make the chain that contains the elements needed to perform
857 * We add a tee as the first element so that we can link the visualisation chain
858 * to it when requested.
860 * +-----------------------------------------------------------------------+
862 * | +-----+ +---------+ +----------+ +---------+ +---------+ |
863 * | | tee | |audioconv| |audioscale| | volume | |audiosink| |
864 * | +-sink src-sink src-sink src-sink src-sink | |
865 * | | +-----+ +---------+ +----------+ +---------+ +---------+ |
867 * +-----------------------------------------------------------------------+
869 static GstPlayChain *
870 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
872 GstPlayAudioChain *chain;
877 chain = g_new0 (GstPlayAudioChain, 1);
878 chain->chain.playsink = gst_object_ref (playsink);
880 if (playsink->audio_sink) {
881 chain->sink = playsink->audio_sink;
883 chain->sink = gst_element_factory_make ("autoaudiosink", "audiosink");
884 if (chain->sink == NULL) {
885 chain->sink = gst_element_factory_make ("alsasink", "audiosink");
887 if (chain->sink == NULL)
890 chain->chain.bin = gst_bin_new ("abin");
891 bin = GST_BIN_CAST (chain->chain.bin);
892 gst_object_ref (bin);
893 gst_object_sink (bin);
894 gst_bin_add (bin, chain->sink);
897 chain->tee = gst_element_factory_make ("tee", "atee");
898 gst_bin_add (bin, chain->tee);
900 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
901 if (chain->conv == NULL)
902 goto no_audioconvert;
903 gst_bin_add (bin, chain->conv);
905 chain->resample = gst_element_factory_make ("audioresample", "aresample");
906 if (chain->resample == NULL)
907 goto no_audioresample;
908 gst_bin_add (bin, chain->resample);
910 chain->teesrc = gst_element_get_request_pad (chain->tee, "src%d");
911 pad = gst_element_get_pad (chain->conv, "sink");
912 gst_pad_link (chain->teesrc, pad);
913 gst_object_unref (pad);
915 res = gst_element_link_pads (chain->conv, "src", chain->resample, "sink");
917 if (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
918 chain->volume = gst_element_factory_make ("volume", "volume");
919 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
920 gst_bin_add (bin, chain->volume);
923 gst_element_link_pads (chain->resample, "src", chain->volume, "sink");
924 res &= gst_element_link_pads (chain->volume, "src", chain->sink, NULL);
926 res &= gst_element_link_pads (chain->resample, "src", chain->sink, NULL);
931 pad = gst_element_get_pad (chain->tee, "sink");
933 pad = gst_element_get_pad (chain->sink, "sink");
935 chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad);
936 gst_object_unref (pad);
937 gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad);
939 return (GstPlayChain *) chain;
944 post_missing_element_message (playsink, "autoaudiosink");
945 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
946 (_("Both autoaudiosink and alsasink elements are missing.")), (NULL));
947 free_chain ((GstPlayChain *) chain);
952 post_missing_element_message (playsink, "audioconvert");
953 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
954 (_("Missing element '%s' - check your GStreamer installation."),
955 "audioconvert"), ("possibly a liboil version mismatch?"));
956 free_chain ((GstPlayChain *) chain);
962 post_missing_element_message (playsink, "audioresample");
963 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
964 (_("Missing element '%s' - check your GStreamer installation."),
965 "audioresample"), ("possibly a liboil version mismatch?"));
966 free_chain ((GstPlayChain *) chain);
971 GST_ELEMENT_ERROR (playsink, CORE, PAD,
972 (NULL), ("Failed to configure the audio sink."));
973 free_chain ((GstPlayChain *) chain);
979 * +--------------------------------------------------+
981 * | +--------+ +------------+ +-------+ |
982 * | | vqueue | | audioconv | | vis | |
983 * | +-sink src-sink + samp src-sink src-+ |
984 * | | +--------+ +------------+ +-------+ | |
986 * +--------------------------------------------------+
990 static GstPlayChain *
991 gen_vis_chain (GstPlaySink * playsink)
993 GstPlayVisChain *chain;
998 chain = g_new0 (GstPlayVisChain, 1);
999 chain->chain.playsink = gst_object_ref (playsink);
1001 chain->chain.bin = gst_bin_new ("abin");
1002 bin = GST_BIN_CAST (chain->chain.bin);
1003 gst_object_ref (bin);
1004 gst_object_sink (bin);
1006 chain->queue = gst_element_factory_make ("queue", "visqueue");
1007 gst_bin_add (bin, chain->queue);
1009 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1010 if (chain->conv == NULL)
1011 goto no_audioconvert;
1012 gst_bin_add (bin, chain->conv);
1014 chain->resample = gst_element_factory_make ("audioresample", "aresample");
1015 if (chain->resample == NULL)
1016 goto no_audioresample;
1017 gst_bin_add (bin, chain->resample);
1019 if (playsink->visualisation) {
1020 chain->vis = playsink->visualisation;
1022 chain->vis = gst_element_factory_make ("goom", "vis");
1026 gst_bin_add (bin, chain->vis);
1028 res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink");
1029 res &= gst_element_link_pads (chain->conv, "src", chain->resample, "sink");
1030 res &= gst_element_link_pads (chain->resample, "src", chain->vis, "sink");
1034 pad = gst_element_get_pad (chain->queue, "sink");
1035 chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad);
1036 gst_object_unref (pad);
1037 gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad);
1039 pad = gst_element_get_pad (chain->vis, "src");
1040 chain->srcpad = gst_ghost_pad_new ("src", pad);
1041 gst_object_unref (pad);
1042 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1044 return (GstPlayChain *) chain;
1049 post_missing_element_message (playsink, "audioconvert");
1050 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1051 (_("Missing element '%s' - check your GStreamer installation."),
1052 "audioconvert"), ("possibly a liboil version mismatch?"));
1053 free_chain ((GstPlayChain *) chain);
1058 post_missing_element_message (playsink, "audioresample");
1059 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1060 (_("Missing element '%s' - check your GStreamer installation."),
1061 "audioresample"), (NULL));
1062 free_chain ((GstPlayChain *) chain);
1067 post_missing_element_message (playsink, "goom");
1068 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1069 (_("Missing element '%s' - check your GStreamer installation."),
1071 free_chain ((GstPlayChain *) chain);
1076 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1077 (NULL), ("Failed to configure the visualisation element."));
1078 free_chain ((GstPlayChain *) chain);
1086 activate_vis (GstPlaySink * playsink, gboolean activate)
1088 /* need to have an audio chain */
1089 if (!playsink->audiochain || !playsink->vischain)
1092 if (playsink->vischain->activated == activate)
1096 /* activation: Add the vis chain to the sink bin . Take a new srcpad from
1097 * the tee of the audio chain and link it to the sinkpad of the vis chain.
1101 /* deactivation: release the srcpad from the tee of the audio chain. Set the
1102 * vis chain to NULL and remove it from the sink bin */
1109 /* this function is called when all the request pads are requested and when we
1110 * have to construct the final pipeline.
1113 gst_play_sink_reconfigure (GstPlaySink * playsink)
1117 GST_DEBUG_OBJECT (playsink, "reconfiguring");
1119 GST_PLAY_SINK_LOCK (playsink);
1120 GST_OBJECT_LOCK (playsink);
1121 flags = playsink->flags;
1122 GST_OBJECT_UNLOCK (playsink);
1124 if (flags & GST_PLAY_FLAG_AUDIO && playsink->audio_pad) {
1125 if (!playsink->audiochain)
1126 playsink->audiochain =
1127 gen_audio_chain (playsink, playsink->audio_pad_raw);
1128 add_chain (playsink->audiochain, TRUE);
1129 activate_chain (playsink->audiochain, TRUE);
1130 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->audio_pad),
1131 playsink->audiochain->sinkpad);
1133 if (playsink->audiochain) {
1134 add_chain (playsink->audiochain, FALSE);
1135 activate_chain (playsink->audiochain, FALSE);
1137 if (playsink->audio_pad)
1138 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->audio_pad), NULL);
1141 if (flags & GST_PLAY_FLAG_VIDEO && playsink->video_pad) {
1142 if (!playsink->videochain)
1143 playsink->videochain =
1144 gen_video_chain (playsink, playsink->video_pad_raw);
1145 add_chain (playsink->videochain, TRUE);
1146 activate_chain (playsink->videochain, TRUE);
1147 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1148 playsink->videochain->sinkpad);
1150 if (playsink->videochain) {
1151 add_chain (playsink->videochain, FALSE);
1152 activate_chain (playsink->videochain, FALSE);
1154 if (playsink->video_pad)
1155 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1157 GST_PLAY_SINK_UNLOCK (playsink);
1163 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
1165 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
1167 GST_OBJECT_LOCK (playsink);
1168 playsink->flags = flags;
1169 GST_OBJECT_UNLOCK (playsink);
1175 gst_play_sink_get_flags (GstPlaySink * playsink)
1179 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
1181 GST_OBJECT_LOCK (playsink);
1182 res = playsink->flags;
1183 GST_OBJECT_UNLOCK (playsink);
1190 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
1193 gboolean created = FALSE;
1194 gboolean raw = FALSE;
1196 GST_PLAY_SINK_LOCK (playsink);
1198 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
1200 case GST_PLAY_SINK_TYPE_AUDIO:
1201 if (!playsink->audio_pad) {
1202 playsink->audio_pad =
1203 gst_ghost_pad_new_no_target ("audio_sink", GST_PAD_SINK);
1206 playsink->audio_pad_raw = raw;
1207 res = playsink->audio_pad;
1209 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
1211 case GST_PLAY_SINK_TYPE_VIDEO:
1212 if (!playsink->video_pad) {
1213 playsink->video_pad =
1214 gst_ghost_pad_new_no_target ("video_sink", GST_PAD_SINK);
1217 playsink->video_pad_raw = raw;
1218 res = playsink->video_pad;
1220 case GST_PLAY_SINK_TYPE_TEXT:
1221 if (!playsink->text_pad) {
1222 playsink->text_pad =
1223 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
1226 res = playsink->text_pad;
1232 GST_PLAY_SINK_UNLOCK (playsink);
1234 if (created && res) {
1235 gst_pad_set_active (res, TRUE);
1236 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
1243 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
1245 GstPad **res = NULL;
1247 GST_PLAY_SINK_LOCK (playsink);
1248 if (pad == playsink->video_pad) {
1249 res = &playsink->video_pad;
1250 } else if (pad == playsink->audio_pad) {
1251 res = &playsink->audio_pad;
1252 } else if (pad == playsink->text_pad) {
1253 res = &playsink->text_pad;
1255 GST_PLAY_SINK_UNLOCK (playsink);
1258 gst_pad_set_active (*res, FALSE);
1259 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
1264 /* Send an event to our sinks until one of them works; don't then send to the
1265 * remaining sinks (unlike GstBin)
1268 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
1270 gboolean res = TRUE;
1272 if (playsink->audiochain) {
1273 gst_event_ref (event);
1274 if ((res = gst_element_send_event (playsink->audiochain->bin, event))) {
1275 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
1278 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
1280 if (playsink->videochain) {
1281 gst_event_ref (event);
1282 if ((res = gst_element_send_event (playsink->videochain->bin, event))) {
1283 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
1286 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
1289 gst_event_unref (event);
1293 /* We only want to send the event to a single sink (overriding GstBin's
1294 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
1295 * events appropriately. So, this is a messy duplication of code. */
1297 gst_play_sink_send_event (GstElement * element, GstEvent * event)
1299 gboolean res = FALSE;
1300 GstEventType event_type = GST_EVENT_TYPE (event);
1302 switch (event_type) {
1303 case GST_EVENT_SEEK:
1304 GST_DEBUG_OBJECT (element, "Sending seek event to a sink");
1305 res = gst_play_sink_send_event_to_sink (GST_PLAY_SINK (element), event);
1308 res = parent_class->send_event (element, event);
1314 static GstStateChangeReturn
1315 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
1317 GstStateChangeReturn ret;
1318 GstPlaySink *playsink;
1320 playsink = GST_PLAY_SINK (element);
1322 switch (transition) {
1323 case GST_STATE_CHANGE_READY_TO_PAUSED:
1329 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1330 if (ret == GST_STATE_CHANGE_FAILURE)
1333 switch (transition) {
1334 case GST_STATE_CHANGE_READY_TO_PAUSED:
1336 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1337 /* FIXME Release audio device when we implement that */
1339 case GST_STATE_CHANGE_PAUSED_TO_READY:
1340 /* remove sinks we added */
1341 if (playsink->videochain) {
1342 activate_chain (playsink->videochain, FALSE);
1343 add_chain (playsink->videochain, FALSE);
1345 if (playsink->audiochain) {
1346 activate_chain (playsink->audiochain, FALSE);
1347 add_chain (playsink->audiochain, FALSE);