diff options
Diffstat (limited to 'gst/playback/gstplaysink.c')
-rw-r--r-- | gst/playback/gstplaysink.c | 2756 |
1 files changed, 0 insertions, 2756 deletions
diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c deleted file mode 100644 index 68f56b84..00000000 --- a/gst/playback/gstplaysink.c +++ /dev/null @@ -1,2756 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> -#include <gst/gst.h> - -#include <gst/gst-i18n-plugin.h> -#include <gst/pbutils/pbutils.h> - -#include "gstplaysink.h" - -GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug); -#define GST_CAT_DEFAULT gst_play_sink_debug - -#define VOLUME_MAX_DOUBLE 10.0 - -#define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \ - GST_PLAY_FLAG_SOFT_VOLUME - -#define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c)) - -/* holds the common data fields for the audio and video pipelines. We keep them - * in a structure to more easily have all the info available. */ -typedef struct -{ - GstPlaySink *playsink; - GstElement *bin; - gboolean added; - gboolean activated; - gboolean raw; -} GstPlayChain; - -typedef struct -{ - GstPlayChain chain; - GstPad *sinkpad; - GstElement *queue; - GstElement *conv; - GstElement *resample; - GstElement *volume; /* element with the volume property */ - gboolean sink_volume; /* if the volume was provided by the sink */ - GstElement *mute; /* element with the mute property */ - GstElement *sink; -} GstPlayAudioChain; - -typedef struct -{ - GstPlayChain chain; - GstPad *sinkpad; - GstElement *queue; - GstElement *conv; - GstElement *scale; - GstElement *sink; - gboolean async; -} GstPlayVideoChain; - -typedef struct -{ - GstPlayChain chain; - GstPad *sinkpad; - GstElement *queue; - GstElement *conv; - GstElement *resample; - GstPad *blockpad; /* srcpad of resample, used for switching the vis */ - GstPad *vissinkpad; /* visualisation sinkpad, */ - GstElement *vis; - GstPad *vissrcpad; /* visualisation srcpad, */ - GstPad *srcpad; /* outgoing srcpad, used to connect to the next - * chain */ -} GstPlayVisChain; - -typedef struct -{ - GstPlayChain chain; - GstPad *sinkpad; - GstElement *queue; - GstElement *identity; - GstElement *overlay; - GstPad *videosinkpad; - GstPad *textsinkpad; - GstPad *srcpad; /* outgoing srcpad, used to connect to the next - * chain */ - GstElement *sink; /* custom sink to receive subtitle buffers */ -} GstPlayTextChain; - -#define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock) -#define GST_PLAY_SINK_LOCK(playsink) g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)) -#define GST_PLAY_SINK_UNLOCK(playsink) g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)) - -struct _GstPlaySink -{ - GstBin bin; - - GStaticRecMutex lock; - - gboolean async_pending; - gboolean need_async_start; - - GstPlayFlags flags; - - /* chains */ - GstPlayAudioChain *audiochain; - GstPlayVideoChain *videochain; - GstPlayVisChain *vischain; - GstPlayTextChain *textchain; - - /* audio */ - GstPad *audio_pad; - gboolean audio_pad_raw; - /* audio tee */ - GstElement *audio_tee; - GstPad *audio_tee_sink; - GstPad *audio_tee_asrc; - GstPad *audio_tee_vissrc; - /* video */ - GstPad *video_pad; - gboolean video_pad_raw; - /* text */ - GstPad *text_pad; - - /* properties */ - GstElement *audio_sink; - GstElement *video_sink; - GstElement *visualisation; - GstElement *text_sink; - gdouble volume; - gboolean mute; - gchar *font_desc; /* font description */ - gchar *subtitle_encoding; /* subtitle encoding */ - guint connection_speed; /* connection speed in bits/sec (0 = unknown) */ - gint count; - gboolean volume_changed; /* volume/mute changed while no audiochain */ - gboolean mute_changed; /* ... has been created yet */ -}; - -struct _GstPlaySinkClass -{ - GstBinClass parent_class; - - gboolean (*reconfigure) (GstPlaySink * playsink); -}; - -static GstStaticPadTemplate audiorawtemplate = -GST_STATIC_PAD_TEMPLATE ("audio_raw_sink", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); -static GstStaticPadTemplate audiotemplate = -GST_STATIC_PAD_TEMPLATE ("audio_sink", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); -static GstStaticPadTemplate videorawtemplate = -GST_STATIC_PAD_TEMPLATE ("video_raw_sink", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); -static GstStaticPadTemplate videotemplate = -GST_STATIC_PAD_TEMPLATE ("video_sink", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); -static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); - -/* props */ -enum -{ - PROP_0, - PROP_FLAGS, - PROP_MUTE, - PROP_VOLUME, - PROP_FONT_DESC, - PROP_SUBTITLE_ENCODING, - PROP_VIS_PLUGIN, - PROP_LAST -}; - -/* signals */ -enum -{ - LAST_SIGNAL -}; - -static void gst_play_sink_class_init (GstPlaySinkClass * klass); -static void gst_play_sink_init (GstPlaySink * playsink); -static void gst_play_sink_dispose (GObject * object); -static void gst_play_sink_finalize (GObject * object); -static void gst_play_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * spec); -static void gst_play_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * spec); - -static GstPad *gst_play_sink_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name); -static void gst_play_sink_release_request_pad (GstElement * element, - GstPad * pad); -static gboolean gst_play_sink_send_event (GstElement * element, - GstEvent * event); -static GstStateChangeReturn gst_play_sink_change_state (GstElement * element, - GstStateChange transition); - -static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message); - -/* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */ - -G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN); - -static void -gst_play_sink_class_init (GstPlaySinkClass * klass) -{ - GObjectClass *gobject_klass; - GstElementClass *gstelement_klass; - GstBinClass *gstbin_klass; - - gobject_klass = (GObjectClass *) klass; - gstelement_klass = (GstElementClass *) klass; - gstbin_klass = (GstBinClass *) klass; - - gobject_klass->dispose = gst_play_sink_dispose; - gobject_klass->finalize = gst_play_sink_finalize; - gobject_klass->set_property = gst_play_sink_set_property; - gobject_klass->get_property = gst_play_sink_get_property; - - - /** - * GstPlaySink:flags - * - * Control the behaviour of playsink. - */ - g_object_class_install_property (gobject_klass, PROP_FLAGS, - g_param_spec_flags ("flags", "Flags", "Flags to control behaviour", - GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstPlaySink:volume: - * - * Get or set the current audio stream volume. 1.0 means 100%, - * 0.0 means mute. This uses a linear volume scale. - * - */ - g_object_class_install_property (gobject_klass, PROP_VOLUME, - g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%", - 0.0, VOLUME_MAX_DOUBLE, 1.0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, PROP_MUTE, - g_param_spec_boolean ("mute", "Mute", - "Mute the audio channel without changing the volume", FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, PROP_FONT_DESC, - g_param_spec_string ("subtitle-font-desc", - "Subtitle font description", - "Pango font description of font " - "to be used for subtitle rendering", NULL, - G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING, - g_param_spec_string ("subtitle-encoding", "subtitle encoding", - "Encoding to assume if input subtitles are not in UTF-8 encoding. " - "If not set, the GST_SUBTITLE_ENCODING environment variable will " - "be checked for an encoding to use. If that is not set either, " - "ISO-8859-15 will be assumed.", NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN, - g_param_spec_object ("vis-plugin", "Vis plugin", - "the visualization element to use (NULL = default)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass, - reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN, - 0, G_TYPE_NONE); - - gst_element_class_add_pad_template (gstelement_klass, - gst_static_pad_template_get (&audiorawtemplate)); - gst_element_class_add_pad_template (gstelement_klass, - gst_static_pad_template_get (&audiotemplate)); - gst_element_class_add_pad_template (gstelement_klass, - gst_static_pad_template_get (&videorawtemplate)); - gst_element_class_add_pad_template (gstelement_klass, - gst_static_pad_template_get (&videotemplate)); - gst_element_class_add_pad_template (gstelement_klass, - gst_static_pad_template_get (&texttemplate)); - gst_element_class_set_details_simple (gstelement_klass, "Player Sink", - "Generic/Bin/Sink", - "Convenience sink for multiple streams", - "Wim Taymans <wim.taymans@gmail.com>"); - - gstelement_klass->change_state = - GST_DEBUG_FUNCPTR (gst_play_sink_change_state); - gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event); - gstelement_klass->request_new_pad = - GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad); - gstelement_klass->release_pad = - GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad); - - gstbin_klass->handle_message = - GST_DEBUG_FUNCPTR (gst_play_sink_handle_message); - - klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure); -} - -static void -gst_play_sink_init (GstPlaySink * playsink) -{ - /* init groups */ - playsink->video_sink = NULL; - playsink->audio_sink = NULL; - playsink->visualisation = NULL; - playsink->text_sink = NULL; - playsink->volume = 1.0; - playsink->font_desc = NULL; - playsink->subtitle_encoding = NULL; - playsink->flags = DEFAULT_FLAGS; - - g_static_rec_mutex_init (&playsink->lock); - GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK); -} - -static void -free_chain (GstPlayChain * chain) -{ - if (chain) { - if (chain->bin) - gst_object_unref (chain->bin); - g_free (chain); - } -} - -static void -gst_play_sink_dispose (GObject * object) -{ - GstPlaySink *playsink; - - playsink = GST_PLAY_SINK (object); - - if (playsink->audio_sink != NULL) { - gst_element_set_state (playsink->audio_sink, GST_STATE_NULL); - gst_object_unref (playsink->audio_sink); - playsink->audio_sink = NULL; - } - if (playsink->video_sink != NULL) { - gst_element_set_state (playsink->video_sink, GST_STATE_NULL); - gst_object_unref (playsink->video_sink); - playsink->video_sink = NULL; - } - if (playsink->visualisation != NULL) { - gst_element_set_state (playsink->visualisation, GST_STATE_NULL); - gst_object_unref (playsink->visualisation); - playsink->visualisation = NULL; - } - if (playsink->text_sink != NULL) { - gst_element_set_state (playsink->text_sink, GST_STATE_NULL); - gst_object_unref (playsink->text_sink); - playsink->text_sink = NULL; - } - - free_chain ((GstPlayChain *) playsink->videochain); - playsink->videochain = NULL; - free_chain ((GstPlayChain *) playsink->audiochain); - playsink->audiochain = NULL; - free_chain ((GstPlayChain *) playsink->vischain); - playsink->vischain = NULL; - free_chain ((GstPlayChain *) playsink->textchain); - playsink->textchain = NULL; - - if (playsink->audio_tee_sink) { - gst_object_unref (playsink->audio_tee_sink); - playsink->audio_tee_sink = NULL; - } - - if (playsink->audio_tee_vissrc) { - gst_element_release_request_pad (playsink->audio_tee, - playsink->audio_tee_vissrc); - gst_object_unref (playsink->audio_tee_vissrc); - playsink->audio_tee_vissrc = NULL; - } - - if (playsink->audio_tee_asrc) { - gst_element_release_request_pad (playsink->audio_tee, - playsink->audio_tee_asrc); - gst_object_unref (playsink->audio_tee_asrc); - playsink->audio_tee_asrc = NULL; - } - - g_free (playsink->font_desc); - playsink->font_desc = NULL; - - g_free (playsink->subtitle_encoding); - playsink->subtitle_encoding = NULL; - - G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object); -} - -static void -gst_play_sink_finalize (GObject * object) -{ - GstPlaySink *playsink; - - playsink = GST_PLAY_SINK (object); - - g_static_rec_mutex_free (&playsink->lock); - - G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object); -} - -void -gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type, - GstElement * sink) -{ - GstElement **elem = NULL, *old = NULL; - - GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type); - - GST_PLAY_SINK_LOCK (playsink); - switch (type) { - case GST_PLAY_SINK_TYPE_AUDIO: - case GST_PLAY_SINK_TYPE_AUDIO_RAW: - elem = &playsink->audio_sink; - break; - case GST_PLAY_SINK_TYPE_VIDEO: - case GST_PLAY_SINK_TYPE_VIDEO_RAW: - elem = &playsink->video_sink; - break; - case GST_PLAY_SINK_TYPE_TEXT: - elem = &playsink->text_sink; - break; - default: - break; - } - if (elem) { - old = *elem; - if (sink) - gst_object_ref (sink); - *elem = sink; - } - GST_PLAY_SINK_UNLOCK (playsink); - - if (old) { - if (old != sink) - gst_element_set_state (old, GST_STATE_NULL); - gst_object_unref (old); - } -} - -GstElement * -gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type) -{ - GstElement *result = NULL; - GstElement *elem = NULL, *chainp = NULL; - - GST_PLAY_SINK_LOCK (playsink); - switch (type) { - case GST_PLAY_SINK_TYPE_AUDIO: - { - GstPlayAudioChain *chain; - if ((chain = (GstPlayAudioChain *) playsink->audiochain)) - chainp = chain->sink; - elem = playsink->audio_sink; - break; - } - case GST_PLAY_SINK_TYPE_VIDEO: - { - GstPlayVideoChain *chain; - if ((chain = (GstPlayVideoChain *) playsink->videochain)) - chainp = chain->sink; - elem = playsink->video_sink; - break; - } - case GST_PLAY_SINK_TYPE_TEXT: - { - GstPlayTextChain *chain; - if ((chain = (GstPlayTextChain *) playsink->textchain)) - chainp = chain->sink; - elem = playsink->text_sink; - break; - } - default: - break; - } - if (chainp) { - /* we have an active chain with a sink, get the sink */ - result = gst_object_ref (chainp); - } - /* nothing found, return last configured sink */ - if (result == NULL && elem) - result = gst_object_ref (elem); - GST_PLAY_SINK_UNLOCK (playsink); - - return result; -} - -static void -gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked, - gpointer user_data) -{ - GstPlaySink *playsink; - - playsink = GST_PLAY_SINK (user_data); - /* nothing to do here, we need a dummy callback here to make the async call - * non-blocking. */ - GST_DEBUG_OBJECT (playsink, "vis pad unblocked"); -} - -static void -gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked, - gpointer user_data) -{ - GstPlaySink *playsink; - GstPlayVisChain *chain; - - playsink = GST_PLAY_SINK (user_data); - - GST_PLAY_SINK_LOCK (playsink); - GST_DEBUG_OBJECT (playsink, "vis pad blocked"); - /* now try to change the plugin in the running vis chain */ - if (!(chain = (GstPlayVisChain *) playsink->vischain)) - goto done; - - /* unlink the old plugin and unghost the pad */ - gst_pad_unlink (chain->blockpad, chain->vissinkpad); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL); - - /* set the old plugin to NULL and remove */ - gst_element_set_state (chain->vis, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis); - - /* add new plugin and set state to playing */ - chain->vis = playsink->visualisation; - gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis); - gst_element_set_state (chain->vis, GST_STATE_PLAYING); - - /* get pads */ - chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink"); - chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src"); - - /* link pads */ - gst_pad_link (chain->blockpad, chain->vissinkpad); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), - chain->vissrcpad); - -done: - /* Unblock the pad */ - gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked, - playsink); - GST_PLAY_SINK_UNLOCK (playsink); -} - -void -gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis) -{ - GstPlayVisChain *chain; - - /* setting NULL means creating the default vis plugin */ - if (vis == NULL) - vis = gst_element_factory_make ("goom", "vis"); - - /* simply return if we don't have a vis plugin here */ - if (vis == NULL) - return; - - GST_PLAY_SINK_LOCK (playsink); - /* first store the new vis */ - if (playsink->visualisation) - gst_object_unref (playsink->visualisation); - /* take ownership */ - gst_object_ref_sink (vis); - playsink->visualisation = vis; - - /* now try to change the plugin in the running vis chain, if we have no chain, - * we don't bother, any future vis chain will be created with the new vis - * plugin. */ - if (!(chain = (GstPlayVisChain *) playsink->vischain)) - goto done; - - /* block the pad, the next time the callback is called we can change the - * visualisation. It's possible that this never happens or that the pad was - * already blocked. If the callback never happens, we don't have new data so - * we don't need the new vis plugin. If the pad was already blocked, the - * function returns FALSE but the previous pad block will do the right thing - * anyway. */ - GST_DEBUG_OBJECT (playsink, "blocking vis pad"); - gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked, - playsink); -done: - GST_PLAY_SINK_UNLOCK (playsink); - - return; -} - -GstElement * -gst_play_sink_get_vis_plugin (GstPlaySink * playsink) -{ - GstElement *result = NULL; - GstPlayVisChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - if ((chain = (GstPlayVisChain *) playsink->vischain)) { - /* we have an active chain, get the sink */ - if (chain->vis) - result = gst_object_ref (chain->vis); - } - /* nothing found, return last configured sink */ - if (result == NULL && playsink->visualisation) - result = gst_object_ref (playsink->visualisation); - GST_PLAY_SINK_UNLOCK (playsink); - - return result; -} - -void -gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume) -{ - GstPlayAudioChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - playsink->volume = volume; - chain = (GstPlayAudioChain *) playsink->audiochain; - if (chain && chain->volume) { - GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%" - GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume, - chain->mute, volume, playsink->mute); - /* if there is a mute element or we are not muted, set the volume */ - if (chain->mute || !playsink->mute) - g_object_set (chain->volume, "volume", volume, NULL); - } else { - GST_LOG_OBJECT (playsink, "no volume element"); - playsink->volume_changed = TRUE; - } - GST_PLAY_SINK_UNLOCK (playsink); -} - -gdouble -gst_play_sink_get_volume (GstPlaySink * playsink) -{ - gdouble result; - GstPlayAudioChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - chain = (GstPlayAudioChain *) playsink->audiochain; - result = playsink->volume; - if (chain && chain->volume) { - if (chain->mute || !playsink->mute) { - g_object_get (chain->volume, "volume", &result, NULL); - playsink->volume = result; - } - } - GST_PLAY_SINK_UNLOCK (playsink); - - return result; -} - -void -gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute) -{ - GstPlayAudioChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - playsink->mute = mute; - chain = (GstPlayAudioChain *) playsink->audiochain; - if (chain) { - if (chain->mute) { - g_object_set (chain->mute, "mute", mute, NULL); - } else if (chain->volume) { - if (mute) - g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL); - else - g_object_set (chain->volume, "volume", (gdouble) playsink->volume, - NULL); - } - } else { - playsink->mute_changed = TRUE; - } - GST_PLAY_SINK_UNLOCK (playsink); -} - -gboolean -gst_play_sink_get_mute (GstPlaySink * playsink) -{ - gboolean result; - GstPlayAudioChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - chain = (GstPlayAudioChain *) playsink->audiochain; - if (chain && chain->mute) { - g_object_get (chain->mute, "mute", &result, NULL); - playsink->mute = result; - } else { - result = playsink->mute; - } - GST_PLAY_SINK_UNLOCK (playsink); - - return result; -} - -static void -post_missing_element_message (GstPlaySink * playsink, const gchar * name) -{ - GstMessage *msg; - - msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name); - gst_element_post_message (GST_ELEMENT_CAST (playsink), msg); -} - -static gboolean -add_chain (GstPlayChain * chain, gboolean add) -{ - if (chain->added == add) - return TRUE; - - if (add) - gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin); - else { - gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin); - /* we don't want to lose our sink status */ - GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK); - } - - chain->added = add; - - return TRUE; -} - -static gboolean -activate_chain (GstPlayChain * chain, gboolean activate) -{ - GstState state; - - if (chain->activated == activate) - return TRUE; - - GST_OBJECT_LOCK (chain->playsink); - state = GST_STATE_TARGET (chain->playsink); - GST_OBJECT_UNLOCK (chain->playsink); - - if (activate) - gst_element_set_state (chain->bin, state); - else - gst_element_set_state (chain->bin, GST_STATE_NULL); - - chain->activated = activate; - - return TRUE; -} - -static gboolean -element_is_sink (GstElement * element) -{ - gboolean is_sink; - - GST_OBJECT_LOCK (element); - is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK); - GST_OBJECT_UNLOCK (element); - - GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no"); - return is_sink; -} - -static gboolean -element_has_property (GstElement * element, const gchar * pname, GType type) -{ - GParamSpec *pspec; - - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname); - - if (pspec == NULL) { - GST_DEBUG_OBJECT (element, "no %s property", pname); - return FALSE; - } - - if (type == G_TYPE_INVALID || type == pspec->value_type || - g_type_is_a (pspec->value_type, type)) { - GST_DEBUG_OBJECT (element, "has %s property of type %s", pname, - (type == G_TYPE_INVALID) ? "any type" : g_type_name (type)); - return TRUE; - } - - GST_WARNING_OBJECT (element, "has %s property, but property is of type %s " - "and we expected it to be of type %s", pname, - g_type_name (pspec->value_type), g_type_name (type)); - - return FALSE; -} - -typedef struct -{ - const gchar *prop_name; - GType prop_type; - gboolean need_sink; -} FindPropertyHelper; - -static gint -find_property (GstElement * element, FindPropertyHelper * helper) -{ - if (helper->need_sink && !element_is_sink (element)) { - gst_object_unref (element); - return 1; - } - - if (!element_has_property (element, helper->prop_name, helper->prop_type)) { - gst_object_unref (element); - return 1; - } - - GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name, - (helper->need_sink) ? "sink" : "element"); - return 0; /* keep it */ -} - -/* FIXME: why not move these functions into core? */ -/* find a sink in the hierarchy with a property named @name. This function does - * not increase the refcount of the returned object and thus remains valid as - * long as the bin is valid. */ -static GstElement * -gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj, - const gchar * name, GType expected_type) -{ - GstElement *result = NULL; - GstIterator *it; - - if (element_has_property (obj, name, expected_type)) { - result = obj; - } else if (GST_IS_BIN (obj)) { - FindPropertyHelper helper = { name, expected_type, TRUE }; - - it = gst_bin_iterate_recurse (GST_BIN_CAST (obj)); - result = gst_iterator_find_custom (it, - (GCompareFunc) find_property, &helper); - gst_iterator_free (it); - /* we don't need the extra ref */ - if (result) - gst_object_unref (result); - } - return result; -} - -/* find an object in the hierarchy with a property named @name */ -static GstElement * -gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj, - const gchar * name, GType expected_type) -{ - GstElement *result = NULL; - GstIterator *it; - - if (GST_IS_BIN (obj)) { - FindPropertyHelper helper = { name, expected_type, FALSE }; - - it = gst_bin_iterate_recurse (GST_BIN_CAST (obj)); - result = gst_iterator_find_custom (it, - (GCompareFunc) find_property, &helper); - gst_iterator_free (it); - } else { - if (element_has_property (obj, name, expected_type)) { - result = obj; - gst_object_ref (obj); - } - } - return result; -} - -static void -do_async_start (GstPlaySink * playsink) -{ - GstMessage *message; - - if (!playsink->need_async_start) { - GST_INFO_OBJECT (playsink, "no async_start needed"); - return; - } - - playsink->async_pending = TRUE; - - GST_INFO_OBJECT (playsink, "Sending async_start message"); - message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE); - GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST - (playsink), message); -} - -static void -do_async_done (GstPlaySink * playsink) -{ - GstMessage *message; - - if (playsink->async_pending) { - GST_INFO_OBJECT (playsink, "Sending async_done message"); - message = gst_message_new_async_done (GST_OBJECT_CAST (playsink)); - GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST - (playsink), message); - - playsink->async_pending = FALSE; - } - - playsink->need_async_start = FALSE; -} - -/* try to change the state of an element. This function returns the element when - * the state change could be performed. When this function returns NULL an error - * occured and the element is unreffed if @unref is TRUE. */ -static GstElement * -try_element (GstPlaySink * playsink, GstElement * element, gboolean unref) -{ - GstStateChangeReturn ret; - - if (element) { - ret = gst_element_set_state (element, GST_STATE_READY); - if (ret == GST_STATE_CHANGE_FAILURE) { - GST_DEBUG_OBJECT (playsink, "failed state change.."); - gst_element_set_state (element, GST_STATE_NULL); - if (unref) - gst_object_unref (element); - element = NULL; - } - } - return element; -} - -/* make the element (bin) that contains the elements needed to perform - * video display. - * - * +------------------------------------------------------------+ - * | vbin | - * | +-------+ +----------+ +----------+ +---------+ | - * | | queue | |colorspace| |videoscale| |videosink| | - * | +-sink src-sink src-sink src-sink | | - * | | +-------+ +----------+ +----------+ +---------+ | - * sink-+ | - * +------------------------------------------------------------+ - * - */ -static GstPlayVideoChain * -gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async, - gboolean queue) -{ - GstPlayVideoChain *chain; - GstBin *bin; - GstPad *pad; - GstElement *head = NULL, *prev = NULL, *elem = NULL; - - chain = g_new0 (GstPlayVideoChain, 1); - chain->chain.playsink = playsink; - chain->chain.raw = raw; - - GST_DEBUG_OBJECT (playsink, "making video chain %p", chain); - - if (playsink->video_sink) { - GST_DEBUG_OBJECT (playsink, "trying configured videosink"); - chain->sink = try_element (playsink, playsink->video_sink, FALSE); - } else { - /* only try fallback if no specific sink was chosen */ - if (chain->sink == NULL) { - GST_DEBUG_OBJECT (playsink, "trying autovideosink"); - elem = gst_element_factory_make ("autovideosink", "videosink"); - chain->sink = try_element (playsink, elem, TRUE); - } - if (chain->sink == NULL) { - /* if default sink from config.h is different then try it too */ - if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) { - GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK); - elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink"); - chain->sink = try_element (playsink, elem, TRUE); - } - } - } - if (chain->sink == NULL) - goto no_sinks; - head = chain->sink; - - /* if we can disable async behaviour of the sink, we can avoid adding a - * queue for the audio chain. */ - elem = - gst_play_sink_find_property_sinks (playsink, chain->sink, "async", - G_TYPE_BOOLEAN); - if (elem) { - GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s", - async, GST_ELEMENT_NAME (elem)); - g_object_set (elem, "async", async, NULL); - chain->async = async; - } else { - GST_DEBUG_OBJECT (playsink, "no async property on the sink"); - chain->async = TRUE; - } - - /* create a bin to hold objects, as we create them we add them to this bin so - * that when something goes wrong we only need to unref the bin */ - chain->chain.bin = gst_bin_new ("vbin"); - bin = GST_BIN_CAST (chain->chain.bin); - gst_object_ref_sink (bin); - gst_bin_add (bin, chain->sink); - - if (queue) { - /* decouple decoder from sink, this improves playback quite a lot since the - * decoder can continue while the sink blocks for synchronisation. We don't - * need a lot of buffers as this consumes a lot of memory and we don't want - * too little because else we would be context switching too quickly. */ - chain->queue = gst_element_factory_make ("queue", "vqueue"); - if (chain->queue == NULL) { - post_missing_element_message (playsink, "queue"); - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "queue"), ("video rendering might be suboptimal")); - head = chain->sink; - prev = NULL; - } else { - g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3, - "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL); - gst_bin_add (bin, chain->queue); - head = prev = chain->queue; - } - } else { - head = chain->sink; - prev = NULL; - } - - if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) { - GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace"); - chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv"); - if (chain->conv == NULL) { - post_missing_element_message (playsink, "ffmpegcolorspace"); - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "ffmpegcolorspace"), ("video rendering might fail")); - } else { - gst_bin_add (bin, chain->conv); - if (prev) { - if (!gst_element_link_pads (prev, "src", chain->conv, "sink")) - goto link_failed; - } else { - head = chain->conv; - } - prev = chain->conv; - } - - GST_DEBUG_OBJECT (playsink, "creating videoscale"); - chain->scale = gst_element_factory_make ("videoscale", "vscale"); - if (chain->scale == NULL) { - post_missing_element_message (playsink, "videoscale"); - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "videoscale"), ("possibly a liboil version mismatch?")); - } else { - gst_bin_add (bin, chain->scale); - if (prev) { - if (!gst_element_link_pads (prev, "src", chain->scale, "sink")) - goto link_failed; - } else { - head = chain->scale; - } - prev = chain->scale; - } - } - - if (prev) { - GST_DEBUG_OBJECT (playsink, "linking to sink"); - if (!gst_element_link_pads (prev, "src", chain->sink, NULL)) - goto link_failed; - } - - pad = gst_element_get_static_pad (head, "sink"); - chain->sinkpad = gst_ghost_pad_new ("sink", pad); - gst_object_unref (pad); - - gst_element_add_pad (chain->chain.bin, chain->sinkpad); - - return chain; - - /* ERRORS */ -no_sinks: - { - if (!elem) { - post_missing_element_message (playsink, "autovideosink"); - if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) { - post_missing_element_message (playsink, DEFAULT_VIDEOSINK); - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("Both autovideosink and %s elements are missing."), - DEFAULT_VIDEOSINK), (NULL)); - } else { - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("The autovideosink element is missing.")), (NULL)); - } - } else { - if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) { - GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE, - (_("Both autovideosink and %s elements are not working."), - DEFAULT_VIDEOSINK), (NULL)); - } else { - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("The autovideosink element is not working.")), (NULL)); - } - } - free_chain ((GstPlayChain *) chain); - return NULL; - } -link_failed: - { - GST_ELEMENT_ERROR (playsink, CORE, PAD, - (NULL), ("Failed to configure the video sink.")); - free_chain ((GstPlayChain *) chain); - return NULL; - } -} - -static gboolean -setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async, - gboolean queue) -{ - GstElement *elem; - GstPlayVideoChain *chain; - GstStateChangeReturn ret; - - chain = playsink->videochain; - - /* if the chain was active we don't do anything */ - if (GST_PLAY_CHAIN (chain)->activated == TRUE) - return TRUE; - - if (chain->chain.raw != raw) - return FALSE; - - /* try to set the sink element to READY again */ - ret = gst_element_set_state (chain->sink, GST_STATE_READY); - if (ret == GST_STATE_CHANGE_FAILURE) - return FALSE; - - /* if we can disable async behaviour of the sink, we can avoid adding a - * queue for the audio chain. */ - elem = - gst_play_sink_find_property_sinks (playsink, chain->sink, "async", - G_TYPE_BOOLEAN); - if (elem) { - GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s", - async, GST_ELEMENT_NAME (elem)); - g_object_set (elem, "async", async, NULL); - chain->async = async; - } else { - GST_DEBUG_OBJECT (playsink, "no async property on the sink"); - chain->async = TRUE; - } - return TRUE; -} - -/* make an element for playback of video with subtitles embedded. - * - * +--------------------------------------------+ - * | tbin | - * | +--------+ +-----------------+ | - * | | queue | | subtitleoverlay | | - * video--src sink---video_sink | | - * | +--------+ | src--src - * text------------------text_sink | | - * | +-----------------+ | - * +--------------------------------------------+ - * - */ -static GstPlayTextChain * -gen_text_chain (GstPlaySink * playsink) -{ - GstPlayTextChain *chain; - GstBin *bin; - GstElement *elem; - GstPad *videosinkpad, *textsinkpad, *srcpad; - - chain = g_new0 (GstPlayTextChain, 1); - chain->chain.playsink = playsink; - - GST_DEBUG_OBJECT (playsink, "making text chain %p", chain); - - chain->chain.bin = gst_bin_new ("tbin"); - bin = GST_BIN_CAST (chain->chain.bin); - gst_object_ref_sink (bin); - - videosinkpad = textsinkpad = srcpad = NULL; - - /* first try to hook the text pad to the custom sink */ - if (playsink->text_sink) { - GST_DEBUG_OBJECT (playsink, "trying configured textsink"); - chain->sink = try_element (playsink, playsink->text_sink, FALSE); - if (chain->sink) { - elem = - gst_play_sink_find_property_sinks (playsink, chain->sink, "async", - G_TYPE_BOOLEAN); - if (elem) { - /* make sure the sparse subtitles don't participate in the preroll */ - g_object_set (elem, "async", FALSE, NULL); - /* we have a custom sink, this will be our textsinkpad */ - textsinkpad = gst_element_get_static_pad (chain->sink, "sink"); - if (textsinkpad) { - /* we're all fine now and we can add the sink to the chain */ - GST_DEBUG_OBJECT (playsink, "adding custom text sink"); - gst_bin_add (bin, chain->sink); - } else { - GST_WARNING_OBJECT (playsink, - "can't find a sink pad on custom text sink"); - gst_object_unref (chain->sink); - chain->sink = NULL; - } - /* try to set sync to true but it's no biggie when we can't */ - if ((elem = - gst_play_sink_find_property_sinks (playsink, chain->sink, - "sync", G_TYPE_BOOLEAN))) - g_object_set (elem, "sync", TRUE, NULL); - } else { - GST_WARNING_OBJECT (playsink, - "can't find async property in custom text sink"); - } - } - if (textsinkpad == NULL) { - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Custom text sink element is not usable.")), - ("fallback to default textoverlay")); - } - } - - if (textsinkpad == NULL) { - if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) { - /* make a little queue */ - chain->queue = gst_element_factory_make ("queue", "vqueue"); - if (chain->queue == NULL) { - post_missing_element_message (playsink, "queue"); - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "queue"), ("video rendering might be suboptimal")); - } else { - g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3, - "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL); - gst_bin_add (bin, chain->queue); - videosinkpad = gst_element_get_static_pad (chain->queue, "sink"); - } - - chain->overlay = - gst_element_factory_make ("subtitleoverlay", "suboverlay"); - if (chain->overlay == NULL) { - post_missing_element_message (playsink, "subtitleoverlay"); - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "subtitleoverlay"), ("subtitle rendering disabled")); - } else { - gst_bin_add (bin, chain->overlay); - - g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL); - if (playsink->font_desc) { - g_object_set (G_OBJECT (chain->overlay), "font-desc", - playsink->font_desc, NULL); - } - if (playsink->subtitle_encoding) { - g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding", - playsink->subtitle_encoding, NULL); - } - - gst_element_link_pads (chain->queue, "src", chain->overlay, - "video_sink"); - - textsinkpad = - gst_element_get_static_pad (chain->overlay, "subtitle_sink"); - srcpad = gst_element_get_static_pad (chain->overlay, "src"); - } - } - } - - if (videosinkpad == NULL) { - /* if we still don't have a videosink, we don't have an overlay. the only - * thing we can do is insert an identity and ghost the src - * and sink pads. */ - chain->identity = gst_element_factory_make ("identity", "tidentity"); - if (chain->identity == NULL) { - post_missing_element_message (playsink, "identity"); - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "identity"), (NULL)); - } else { - g_object_set (chain->identity, "signal-handoffs", FALSE, NULL); - g_object_set (chain->identity, "silent", TRUE, NULL); - gst_bin_add (bin, chain->identity); - srcpad = gst_element_get_static_pad (chain->identity, "src"); - videosinkpad = gst_element_get_static_pad (chain->identity, "sink"); - } - } - - /* expose the ghostpads */ - if (videosinkpad) { - chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad); - gst_object_unref (videosinkpad); - gst_element_add_pad (chain->chain.bin, chain->videosinkpad); - } - if (textsinkpad) { - chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad); - gst_object_unref (textsinkpad); - gst_element_add_pad (chain->chain.bin, chain->textsinkpad); - } - if (srcpad) { - chain->srcpad = gst_ghost_pad_new ("src", srcpad); - gst_object_unref (srcpad); - gst_element_add_pad (chain->chain.bin, chain->srcpad); - } - - return chain; -} - -static void -notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink) -{ - gdouble vol; - - g_object_get (object, "volume", &vol, NULL); - playsink->volume = vol; - - g_object_notify (G_OBJECT (playsink), "volume"); -} - -static void -notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink) -{ - gboolean mute; - - g_object_get (object, "mute", &mute, NULL); - playsink->mute = mute; - - g_object_notify (G_OBJECT (playsink), "mute"); -} - -/* make the chain that contains the elements needed to perform - * audio playback. - * - * We add a tee as the first element so that we can link the visualisation chain - * to it when requested. - * - * +-------------------------------------------------------------+ - * | abin | - * | +---------+ +----------+ +---------+ +---------+ | - * | |audioconv| |audioscale| | volume | |audiosink| | - * | +-srck src-sink src-sink src-sink | | - * | | +---------+ +----------+ +---------+ +---------+ | - * sink-+ | - * +-------------------------------------------------------------+ - */ -static GstPlayAudioChain * -gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue) -{ - GstPlayAudioChain *chain; - GstBin *bin; - gboolean have_volume; - GstPad *pad; - GstElement *head, *prev, *elem = NULL; - - chain = g_new0 (GstPlayAudioChain, 1); - chain->chain.playsink = playsink; - chain->chain.raw = raw; - - GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain); - - if (playsink->audio_sink) { - GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT, - playsink->audio_sink); - chain->sink = try_element (playsink, playsink->audio_sink, FALSE); - } else { - /* only try fallback if no specific sink was chosen */ - if (chain->sink == NULL) { - GST_DEBUG_OBJECT (playsink, "trying autoaudiosink"); - elem = gst_element_factory_make ("autoaudiosink", "audiosink"); - chain->sink = try_element (playsink, elem, TRUE); - } - if (chain->sink == NULL) { - /* if default sink from config.h is different then try it too */ - if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) { - GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK); - elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink"); - chain->sink = try_element (playsink, elem, TRUE); - } - } - } - if (chain->sink == NULL) - goto no_sinks; - - chain->chain.bin = gst_bin_new ("abin"); - bin = GST_BIN_CAST (chain->chain.bin); - gst_object_ref_sink (bin); - gst_bin_add (bin, chain->sink); - - if (queue) { - /* we have to add a queue when we need to decouple for the video sink in - * visualisations */ - GST_DEBUG_OBJECT (playsink, "adding audio queue"); - chain->queue = gst_element_factory_make ("queue", "aqueue"); - if (chain->queue == NULL) { - post_missing_element_message (playsink, "queue"); - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "queue"), ("audio playback and visualizations might not work")); - head = chain->sink; - prev = NULL; - } else { - gst_bin_add (bin, chain->queue); - prev = head = chain->queue; - } - } else { - head = chain->sink; - prev = NULL; - } - - /* check if the sink, or something within the sink, has the volume property. - * If it does we don't need to add a volume element. */ - elem = - gst_play_sink_find_property_sinks (playsink, chain->sink, "volume", - G_TYPE_DOUBLE); - if (elem) { - chain->volume = elem; - - g_signal_connect (chain->volume, "notify::volume", - G_CALLBACK (notify_volume_cb), playsink); - - GST_DEBUG_OBJECT (playsink, "the sink has a volume property"); - have_volume = TRUE; - chain->sink_volume = TRUE; - /* if the sink also has a mute property we can use this as well. We'll only - * use the mute property if there is a volume property. We can simulate the - * mute with the volume otherwise. */ - chain->mute = - gst_play_sink_find_property_sinks (playsink, chain->sink, "mute", - G_TYPE_BOOLEAN); - if (chain->mute) { - GST_DEBUG_OBJECT (playsink, "the sink has a mute property"); - g_signal_connect (chain->mute, "notify::mute", - G_CALLBACK (notify_mute_cb), playsink); - } - /* use the sink to control the volume and mute */ - if (playsink->volume_changed) { - g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL); - playsink->volume_changed = FALSE; - } - if (playsink->mute_changed) { - if (chain->mute) { - g_object_set (chain->mute, "mute", playsink->mute, NULL); - } else { - if (playsink->mute) - g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL); - } - playsink->mute_changed = FALSE; - } - } else { - /* no volume, we need to add a volume element when we can */ - GST_DEBUG_OBJECT (playsink, "the sink has no volume property"); - have_volume = FALSE; - chain->sink_volume = FALSE; - } - - if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO)) { - GST_DEBUG_OBJECT (playsink, "creating audioconvert"); - chain->conv = gst_element_factory_make ("audioconvert", "aconv"); - if (chain->conv == NULL) { - post_missing_element_message (playsink, "audioconvert"); - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioconvert"), ("possibly a liboil version mismatch?")); - } else { - gst_bin_add (bin, chain->conv); - if (prev) { - if (!gst_element_link_pads (prev, "src", chain->conv, "sink")) - goto link_failed; - } else { - head = chain->conv; - } - prev = chain->conv; - } - - GST_DEBUG_OBJECT (playsink, "creating audioresample"); - chain->resample = gst_element_factory_make ("audioresample", "aresample"); - if (chain->resample == NULL) { - post_missing_element_message (playsink, "audioresample"); - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioresample"), ("possibly a liboil version mismatch?")); - } else { - gst_bin_add (bin, chain->resample); - if (prev) { - if (!gst_element_link_pads (prev, "src", chain->resample, "sink")) - goto link_failed; - } else { - head = chain->resample; - } - prev = chain->resample; - } - - if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) { - GST_DEBUG_OBJECT (playsink, "creating volume"); - chain->volume = gst_element_factory_make ("volume", "volume"); - if (chain->volume == NULL) { - post_missing_element_message (playsink, "volume"); - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "volume"), ("possibly a liboil version mismatch?")); - } else { - have_volume = TRUE; - - g_signal_connect (chain->volume, "notify::volume", - G_CALLBACK (notify_volume_cb), playsink); - - /* volume also has the mute property */ - chain->mute = chain->volume; - g_signal_connect (chain->mute, "notify::mute", - G_CALLBACK (notify_mute_cb), playsink); - - /* configure with the latest volume and mute */ - g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, - NULL); - g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL); - gst_bin_add (bin, chain->volume); - - if (prev) { - if (!gst_element_link_pads (prev, "src", chain->volume, "sink")) - goto link_failed; - } else { - head = chain->volume; - } - prev = chain->volume; - } - } - } - - if (prev) { - /* we only have to link to the previous element if we have something in - * front of the sink */ - GST_DEBUG_OBJECT (playsink, "linking to sink"); - if (!gst_element_link_pads (prev, "src", chain->sink, NULL)) - goto link_failed; - } - - /* post a warning if we have no way to configure the volume */ - if (!have_volume) { - GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED, - (_("No volume control found")), ("Volume/mute is not available")); - } - - /* and ghost the sinkpad of the headmost element */ - GST_DEBUG_OBJECT (playsink, "ghosting sink pad"); - pad = gst_element_get_static_pad (head, "sink"); - chain->sinkpad = gst_ghost_pad_new ("sink", pad); - gst_object_unref (pad); - gst_element_add_pad (chain->chain.bin, chain->sinkpad); - - return chain; - - /* ERRORS */ -no_sinks: - { - if (!elem) { - post_missing_element_message (playsink, "autoaudiosink"); - if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) { - post_missing_element_message (playsink, DEFAULT_AUDIOSINK); - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("Both autoaudiosink and %s elements are missing."), - DEFAULT_AUDIOSINK), (NULL)); - } else { - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("The autoaudiosink element is missing.")), (NULL)); - } - } else { - if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) { - GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE, - (_("Both autoaudiosink and %s elements are not working."), - DEFAULT_AUDIOSINK), (NULL)); - } else { - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("The autoaudiosink element is not working.")), (NULL)); - } - } - free_chain ((GstPlayChain *) chain); - return NULL; - } -link_failed: - { - GST_ELEMENT_ERROR (playsink, CORE, PAD, - (NULL), ("Failed to configure the audio sink.")); - free_chain ((GstPlayChain *) chain); - return NULL; - } -} - -static gboolean -setup_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue) -{ - GstElement *elem; - GstPlayAudioChain *chain; - GstStateChangeReturn ret; - - chain = playsink->audiochain; - - /* if the chain was active we don't do anything */ - if (GST_PLAY_CHAIN (chain)->activated == TRUE) - return TRUE; - - if (chain->chain.raw != raw) - return FALSE; - - /* try to set the sink element to READY again */ - ret = gst_element_set_state (chain->sink, GST_STATE_READY); - if (ret == GST_STATE_CHANGE_FAILURE) - return FALSE; - - /* check if the sink, or something within the sink, has the volume property. - * If it does we don't need to add a volume element. */ - elem = - gst_play_sink_find_property_sinks (playsink, chain->sink, "volume", - G_TYPE_DOUBLE); - if (elem) { - chain->volume = elem; - - if (playsink->volume_changed) { - GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f", - playsink->volume); - /* use the sink to control the volume */ - g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL); - playsink->volume_changed = FALSE; - } - - g_signal_connect (chain->volume, "notify::volume", - G_CALLBACK (notify_volume_cb), playsink); - /* if the sink also has a mute property we can use this as well. We'll only - * use the mute property if there is a volume property. We can simulate the - * mute with the volume otherwise. */ - chain->mute = - gst_play_sink_find_property_sinks (playsink, chain->sink, "mute", - G_TYPE_BOOLEAN); - if (chain->mute) { - GST_DEBUG_OBJECT (playsink, "the sink has a mute property"); - g_signal_connect (chain->mute, "notify::mute", - G_CALLBACK (notify_mute_cb), playsink); - } - } else { - /* no volume, we need to add a volume element when we can */ - GST_DEBUG_OBJECT (playsink, "the sink has no volume property"); - if (!raw) { - GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control"); - - if (chain->volume) - g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb, - playsink); - if (chain->mute) - g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb, - playsink); - - chain->volume = NULL; - chain->mute = NULL; - } else { - /* both last and current chain are raw audio, there should be a volume - * element already, unless the sink changed from one with a volume - * property to one that hasn't got a volume property, in which case we - * re-generate the chain */ - if (chain->volume == NULL) { - GST_DEBUG_OBJECT (playsink, "no existing volume element to re-use"); - return FALSE; - } - - GST_DEBUG_OBJECT (playsink, "reusing existing volume element"); - } - } - return TRUE; -} - -/* - * +-------------------------------------------------------------------+ - * | visbin | - * | +----------+ +------------+ +----------+ +-------+ | - * | | visqueue | | audioconv | | audiores | | vis | | - * | +-sink src-sink + samp src-sink src-sink src-+ | - * | | +----------+ +------------+ +----------+ +-------+ | | - * sink-+ +-src - * +-------------------------------------------------------------------+ - * - */ -static GstPlayVisChain * -gen_vis_chain (GstPlaySink * playsink) -{ - GstPlayVisChain *chain; - GstBin *bin; - gboolean res; - GstPad *pad; - GstElement *elem; - - chain = g_new0 (GstPlayVisChain, 1); - chain->chain.playsink = playsink; - - GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain); - - chain->chain.bin = gst_bin_new ("visbin"); - bin = GST_BIN_CAST (chain->chain.bin); - gst_object_ref_sink (bin); - - /* we're queuing raw audio here, we can remove this queue when we can disable - * async behaviour in the video sink. */ - chain->queue = gst_element_factory_make ("queue", "visqueue"); - if (chain->queue == NULL) - goto no_queue; - gst_bin_add (bin, chain->queue); - - chain->conv = gst_element_factory_make ("audioconvert", "aconv"); - if (chain->conv == NULL) - goto no_audioconvert; - gst_bin_add (bin, chain->conv); - - chain->resample = gst_element_factory_make ("audioresample", "aresample"); - if (chain->resample == NULL) - goto no_audioresample; - gst_bin_add (bin, chain->resample); - - /* this pad will be used for blocking the dataflow and switching the vis - * plugin */ - chain->blockpad = gst_element_get_static_pad (chain->resample, "src"); - - if (playsink->visualisation) { - GST_DEBUG_OBJECT (playsink, "trying configure vis"); - chain->vis = try_element (playsink, playsink->visualisation, FALSE); - } - if (chain->vis == NULL) { - GST_DEBUG_OBJECT (playsink, "trying goom"); - elem = gst_element_factory_make ("goom", "vis"); - chain->vis = try_element (playsink, elem, TRUE); - } - if (chain->vis == NULL) - goto no_goom; - - gst_bin_add (bin, chain->vis); - - res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink"); - res &= gst_element_link_pads (chain->conv, "src", chain->resample, "sink"); - res &= gst_element_link_pads (chain->resample, "src", chain->vis, "sink"); - if (!res) - goto link_failed; - - chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink"); - chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src"); - - pad = gst_element_get_static_pad (chain->queue, "sink"); - chain->sinkpad = gst_ghost_pad_new ("sink", pad); - gst_object_unref (pad); - gst_element_add_pad (chain->chain.bin, chain->sinkpad); - - chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad); - gst_element_add_pad (chain->chain.bin, chain->srcpad); - - return chain; - - /* ERRORS */ -no_queue: - { - post_missing_element_message (playsink, "queue"); - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "queue"), (NULL)); - free_chain ((GstPlayChain *) chain); - return NULL; - } -no_audioconvert: - { - post_missing_element_message (playsink, "audioconvert"); - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioconvert"), ("possibly a liboil version mismatch?")); - free_chain ((GstPlayChain *) chain); - return NULL; - } -no_audioresample: - { - post_missing_element_message (playsink, "audioresample"); - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioresample"), (NULL)); - free_chain ((GstPlayChain *) chain); - return NULL; - } -no_goom: - { - post_missing_element_message (playsink, "goom"); - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "goom"), (NULL)); - free_chain ((GstPlayChain *) chain); - return NULL; - } -link_failed: - { - GST_ELEMENT_ERROR (playsink, CORE, PAD, - (NULL), ("Failed to configure the visualisation element.")); - free_chain ((GstPlayChain *) chain); - return NULL; - } -} - -/* this function is called when all the request pads are requested and when we - * have to construct the final pipeline. Based on the flags we construct the - * final output pipelines. - */ -gboolean -gst_play_sink_reconfigure (GstPlaySink * playsink) -{ - GstPlayFlags flags; - gboolean need_audio, need_video, need_vis, need_text; - - GST_DEBUG_OBJECT (playsink, "reconfiguring"); - - /* assume we need nothing */ - need_audio = need_video = need_vis = need_text = FALSE; - - GST_PLAY_SINK_LOCK (playsink); - GST_OBJECT_LOCK (playsink); - /* get flags, there are protected with the object lock */ - flags = playsink->flags; - GST_OBJECT_UNLOCK (playsink); - - /* figure out which components we need */ - if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) { - /* we have a text_pad and we need text rendering, in this case we need a - * video_pad to combine the video with the text */ - if (!playsink->video_pad) - goto subs_but_no_video; - - /* we have subtitles and we are requested to show it, we also need to show - * video in this case. */ - need_video = TRUE; - need_text = TRUE; - } else if (((flags & GST_PLAY_FLAG_VIDEO) - || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) { - /* we have video and we are requested to show it */ - need_video = TRUE; - } - if (playsink->audio_pad) { - if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) { - need_audio = TRUE; - } - if (playsink->audio_pad_raw) { - /* only can do vis with raw uncompressed audio */ - if (flags & GST_PLAY_FLAG_VIS && !need_video) { - /* also add video when we add visualisation */ - need_video = TRUE; - need_vis = TRUE; - } - } - } - GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio, - need_video, need_vis, need_text); - - /* set up video pipeline */ - if (need_video) { - gboolean raw, async, queue; - - /* we need a raw sink when we do vis or when we have a raw pad */ - raw = need_vis ? TRUE : playsink->video_pad_raw; - /* we try to set the sink async=FALSE when we need vis, this way we can - * avoid a queue in the audio chain. */ - async = !need_vis; - - /* If subtitles are requested there already is a queue in the video chain */ - queue = (need_text == FALSE); - - GST_DEBUG_OBJECT (playsink, "adding video, raw %d", - playsink->video_pad_raw); - - if (playsink->videochain) { - /* try to reactivate the chain */ - if (!setup_video_chain (playsink, raw, async, queue)) { - add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); - activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); - free_chain ((GstPlayChain *) playsink->videochain); - playsink->videochain = NULL; - } - } - - if (!playsink->videochain) { - playsink->videochain = gen_video_chain (playsink, raw, async, queue); - } - if (playsink->videochain) { - GST_DEBUG_OBJECT (playsink, "adding video chain"); - add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE); - activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE); - /* if we are not part of vis or subtitles, set the ghostpad target */ - if (!need_vis && !need_text && !playsink->textchain) { - GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad"); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), - playsink->videochain->sinkpad); - } - } - } else { - GST_DEBUG_OBJECT (playsink, "no video needed"); - if (playsink->videochain) { - GST_DEBUG_OBJECT (playsink, "removing video chain"); - if (playsink->vischain) { - GstPad *srcpad; - - GST_DEBUG_OBJECT (playsink, "unlinking vis chain"); - - /* also had visualisation, release the tee srcpad before we then - * unlink the video from it */ - if (playsink->audio_tee_vissrc) { - gst_element_release_request_pad (playsink->audio_tee, - playsink->audio_tee_vissrc); - gst_object_unref (playsink->audio_tee_vissrc); - playsink->audio_tee_vissrc = NULL; - } - srcpad = - gst_element_get_static_pad (playsink->vischain->chain.bin, "src"); - gst_pad_unlink (srcpad, playsink->videochain->sinkpad); - } - add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); - activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); - } - if (playsink->video_pad) - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL); - } - - if (need_text) { - GST_DEBUG_OBJECT (playsink, "adding text"); - if (!playsink->textchain) { - GST_DEBUG_OBJECT (playsink, "creating text chain"); - playsink->textchain = gen_text_chain (playsink); - } - if (playsink->textchain) { - GST_DEBUG_OBJECT (playsink, "adding text chain"); - g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE, - NULL); - add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE); - - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), - playsink->textchain->textsinkpad); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), - playsink->textchain->videosinkpad); - gst_pad_link (playsink->textchain->srcpad, playsink->videochain->sinkpad); - - activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE); - } - } else { - GST_DEBUG_OBJECT (playsink, "no text needed"); - /* we have no subtitles/text or we are requested to not show them */ - if (playsink->textchain) { - if (playsink->text_pad == NULL) { - /* no text pad, remove the chain entirely */ - GST_DEBUG_OBJECT (playsink, "removing text chain"); - add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE); - activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE); - } else { - /* we have a chain and a textpad, turn the subtitles off */ - GST_DEBUG_OBJECT (playsink, "turning off the text"); - g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE, - NULL); - } - } - if (!need_video && playsink->video_pad) - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL); - if (playsink->text_pad && !playsink->textchain) - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL); - } - - if (need_audio) { - gboolean raw, queue; - - GST_DEBUG_OBJECT (playsink, "adding audio"); - - /* get a raw sink if we are asked for a raw pad */ - raw = playsink->audio_pad_raw; - if (need_vis && playsink->videochain) { - /* If we are dealing with visualisations, we need to add a queue to - * decouple the audio from the video part. We only have to do this when - * the video part is async=true */ - queue = ((GstPlayVideoChain *) playsink->videochain)->async; - GST_DEBUG_OBJECT (playsink, "need audio queue for vis: %d", queue); - } else { - /* no vis, we can avoid a queue */ - GST_DEBUG_OBJECT (playsink, "don't need audio queue"); - queue = FALSE; - } - - if (playsink->audiochain) { - /* try to reactivate the chain */ - if (!setup_audio_chain (playsink, raw, queue)) { - GST_DEBUG_OBJECT (playsink, "removing current audio chain"); - if (playsink->audio_tee_asrc) { - gst_element_release_request_pad (playsink->audio_tee, - playsink->audio_tee_asrc); - gst_object_unref (playsink->audio_tee_asrc); - playsink->audio_tee_asrc = NULL; - } - add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); - activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); - playsink->audiochain->volume = NULL; - playsink->audiochain->mute = NULL; - free_chain ((GstPlayChain *) playsink->audiochain); - playsink->audiochain = NULL; - playsink->volume_changed = playsink->mute_changed = FALSE; - } - } - - if (!playsink->audiochain) { - GST_DEBUG_OBJECT (playsink, "creating new audio chain"); - playsink->audiochain = gen_audio_chain (playsink, raw, queue); - } - - if (playsink->audiochain) { - GST_DEBUG_OBJECT (playsink, "adding audio chain"); - if (playsink->audio_tee_asrc == NULL) { - playsink->audio_tee_asrc = - gst_element_get_request_pad (playsink->audio_tee, "src%d"); - } - add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE); - activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE); - gst_pad_link (playsink->audio_tee_asrc, playsink->audiochain->sinkpad); - } - } else { - GST_DEBUG_OBJECT (playsink, "no audio needed"); - /* we have no audio or we are requested to not play audio */ - if (playsink->audiochain) { - GST_DEBUG_OBJECT (playsink, "removing audio chain"); - /* release the audio pad */ - if (playsink->audio_tee_asrc) { - gst_element_release_request_pad (playsink->audio_tee, - playsink->audio_tee_asrc); - gst_object_unref (playsink->audio_tee_asrc); - playsink->audio_tee_asrc = NULL; - } - if (playsink->audiochain->sink_volume) { - playsink->audiochain->volume = NULL; - playsink->audiochain->mute = NULL; - } - add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); - activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); - } - } - - if (need_vis) { - GstPad *srcpad; - - if (!playsink->vischain) - playsink->vischain = gen_vis_chain (playsink); - - GST_DEBUG_OBJECT (playsink, "adding visualisation"); - - if (playsink->vischain) { - GST_DEBUG_OBJECT (playsink, "setting up vis chain"); - srcpad = - gst_element_get_static_pad (playsink->vischain->chain.bin, "src"); - add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE); - activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE); - if (playsink->audio_tee_vissrc == NULL) { - playsink->audio_tee_vissrc = - gst_element_get_request_pad (playsink->audio_tee, "src%d"); - } - gst_pad_link (playsink->audio_tee_vissrc, playsink->vischain->sinkpad); - gst_pad_link (srcpad, playsink->videochain->sinkpad); - gst_object_unref (srcpad); - } - } else { - GST_DEBUG_OBJECT (playsink, "no vis needed"); - if (playsink->vischain) { - if (playsink->audio_tee_vissrc) { - gst_element_release_request_pad (playsink->audio_tee, - playsink->audio_tee_vissrc); - gst_object_unref (playsink->audio_tee_vissrc); - playsink->audio_tee_vissrc = NULL; - } - GST_DEBUG_OBJECT (playsink, "removing vis chain"); - add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE); - activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE); - } - } - do_async_done (playsink); - GST_PLAY_SINK_UNLOCK (playsink); - - return TRUE; - - /* ERRORS */ -subs_but_no_video: - { - GST_ELEMENT_ERROR (playsink, STREAM, FORMAT, - (_("Can't play a text file without video.")), - ("Have text pad but no video pad")); - GST_PLAY_SINK_UNLOCK (playsink); - return FALSE; - } -} - -/** - * gst_play_sink_set_flags: - * @playsink: a #GstPlaySink - * @flags: #GstPlayFlags - * - * Configure @flags on @playsink. The flags control the behaviour of @playsink - * when constructing the sink pipelins. - * - * Returns: TRUE if the flags could be configured. - */ -gboolean -gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags) -{ - g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE); - - GST_OBJECT_LOCK (playsink); - playsink->flags = flags; - GST_OBJECT_UNLOCK (playsink); - - return TRUE; -} - -/** - * gst_play_sink_get_flags: - * @playsink: a #GstPlaySink - * - * Get the flags of @playsink. That flags control the behaviour of the sink when - * it constructs the sink pipelines. - * - * Returns: the currently configured #GstPlayFlags. - */ -GstPlayFlags -gst_play_sink_get_flags (GstPlaySink * playsink) -{ - GstPlayFlags res; - - g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0); - - GST_OBJECT_LOCK (playsink); - res = playsink->flags; - GST_OBJECT_UNLOCK (playsink); - - return res; -} - -void -gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc) -{ - GstPlayTextChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - chain = (GstPlayTextChain *) playsink->textchain; - g_free (playsink->font_desc); - playsink->font_desc = g_strdup (desc); - if (chain && chain->overlay) { - g_object_set (chain->overlay, "font-desc", desc, NULL); - } - GST_PLAY_SINK_UNLOCK (playsink); -} - -gchar * -gst_play_sink_get_font_desc (GstPlaySink * playsink) -{ - gchar *result = NULL; - GstPlayTextChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - chain = (GstPlayTextChain *) playsink->textchain; - if (chain && chain->overlay) { - g_object_get (chain->overlay, "font-desc", &result, NULL); - playsink->font_desc = g_strdup (result); - } else { - result = g_strdup (playsink->font_desc); - } - GST_PLAY_SINK_UNLOCK (playsink); - - return result; -} - -void -gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink, - const gchar * encoding) -{ - GstPlayTextChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - chain = (GstPlayTextChain *) playsink->textchain; - g_free (playsink->subtitle_encoding); - playsink->subtitle_encoding = g_strdup (encoding); - if (chain && chain->overlay) { - g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL); - } - GST_PLAY_SINK_UNLOCK (playsink); -} - -gchar * -gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink) -{ - gchar *result = NULL; - GstPlayTextChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - chain = (GstPlayTextChain *) playsink->textchain; - if (chain && chain->overlay) { - g_object_get (chain->overlay, "subtitle-encoding", &result, NULL); - playsink->subtitle_encoding = g_strdup (result); - } else { - result = g_strdup (playsink->subtitle_encoding); - } - GST_PLAY_SINK_UNLOCK (playsink); - - return result; -} - -/** - * gst_play_sink_get_last_frame: - * @playsink: a #GstPlaySink - * - * Get the last displayed frame from @playsink. This frame is in the native - * format of the sink element, the caps on the result buffer contain the format - * of the frame data. - * - * Returns: a #GstBuffer with the frame data or %NULL when no video frame is - * available. - */ -GstBuffer * -gst_play_sink_get_last_frame (GstPlaySink * playsink) -{ - GstBuffer *result = NULL; - GstPlayVideoChain *chain; - - GST_PLAY_SINK_LOCK (playsink); - GST_DEBUG_OBJECT (playsink, "taking last frame"); - /* get the video chain if we can */ - if ((chain = (GstPlayVideoChain *) playsink->videochain)) { - GST_DEBUG_OBJECT (playsink, "found video chain"); - /* see if the chain is active */ - if (chain->chain.activated && chain->sink) { - GstElement *elem; - - GST_DEBUG_OBJECT (playsink, "video chain active and has a sink"); - - /* find and get the last-buffer property now */ - if ((elem = - gst_play_sink_find_property (playsink, chain->sink, - "last-buffer", GST_TYPE_BUFFER))) { - GST_DEBUG_OBJECT (playsink, "getting last-buffer property"); - g_object_get (elem, "last-buffer", &result, NULL); - gst_object_unref (elem); - } - } - } - GST_PLAY_SINK_UNLOCK (playsink); - - return result; -} - -/** - * gst_play_sink_request_pad - * @playsink: a #GstPlaySink - * @type: a #GstPlaySinkType - * - * Create or return a pad of @type. - * - * Returns: a #GstPad of @type or %NULL when the pad could not be created. - */ -GstPad * -gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type) -{ - GstPad *res = NULL; - gboolean created = FALSE; - gboolean raw = FALSE; - gboolean activate = TRUE; - const gchar *pad_name = NULL; - - GST_DEBUG_OBJECT (playsink, "request pad type %d", type); - - GST_PLAY_SINK_LOCK (playsink); - switch (type) { - case GST_PLAY_SINK_TYPE_AUDIO_RAW: - pad_name = "audio_raw_sink"; - raw = TRUE; - case GST_PLAY_SINK_TYPE_AUDIO: - if (pad_name == NULL) - pad_name = "audio_sink"; - if (!playsink->audio_tee) { - GST_LOG_OBJECT (playsink, "creating tee"); - /* create tee when needed. This element will feed the audio sink chain - * and the vis chain. */ - playsink->audio_tee = gst_element_factory_make ("tee", "audiotee"); - if (playsink->audio_tee == NULL) { - post_missing_element_message (playsink, "tee"); - GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "tee"), (NULL)); - res = NULL; - break; - } else { - playsink->audio_tee_sink = - gst_element_get_static_pad (playsink->audio_tee, "sink"); - gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee); - gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED); - } - } else { - gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED); - } - if (!playsink->audio_pad) { - GST_LOG_OBJECT (playsink, "ghosting tee sinkpad"); - playsink->audio_pad = - gst_ghost_pad_new (pad_name, playsink->audio_tee_sink); - created = TRUE; - } - playsink->audio_pad_raw = raw; - res = playsink->audio_pad; - break; - case GST_PLAY_SINK_TYPE_VIDEO_RAW: - pad_name = "video_raw_sink"; - raw = TRUE; - case GST_PLAY_SINK_TYPE_VIDEO: - if (pad_name == NULL) - pad_name = "video_sink"; - if (!playsink->video_pad) { - GST_LOG_OBJECT (playsink, "ghosting videosink"); - playsink->video_pad = - gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK); - created = TRUE; - } - playsink->video_pad_raw = raw; - res = playsink->video_pad; - break; - case GST_PLAY_SINK_TYPE_TEXT: - GST_LOG_OBJECT (playsink, "ghosting text"); - if (!playsink->text_pad) { - playsink->text_pad = - gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK); - created = TRUE; - } - res = playsink->text_pad; - break; - case GST_PLAY_SINK_TYPE_FLUSHING: - { - gchar *padname; - - /* we need a unique padname for the flushing pad. */ - padname = g_strdup_printf ("flushing_%d", playsink->count); - res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK); - g_free (padname); - playsink->count++; - activate = FALSE; - created = TRUE; - break; - } - default: - res = NULL; - break; - } - GST_PLAY_SINK_UNLOCK (playsink); - - if (created && res) { - /* we have to add the pad when it's active or we get an error when the - * element is 'running' */ - gst_pad_set_active (res, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (playsink), res); - if (!activate) - gst_pad_set_active (res, activate); - } - - return res; -} - -static GstPad * -gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ, - const gchar * name) -{ - GstPlaySink *psink; - GstPad *pad; - GstElementClass *klass; - GstPlaySinkType type; - const gchar *tplname; - - g_return_val_if_fail (templ != NULL, NULL); - - GST_DEBUG_OBJECT (element, "name:%s", name); - - psink = GST_PLAY_SINK (element); - klass = GST_ELEMENT_GET_CLASS (element); - tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ); - - /* Figure out the GstPlaySinkType based on the template */ - if (!strcmp (tplname, "audio_sink")) - type = GST_PLAY_SINK_TYPE_AUDIO; - else if (!strcmp (tplname, "aduio_raw_sink")) - type = GST_PLAY_SINK_TYPE_AUDIO_RAW; - else if (!strcmp (tplname, "video_sink")) - type = GST_PLAY_SINK_TYPE_VIDEO; - else if (!strcmp (tplname, "video_raw_sink")) - type = GST_PLAY_SINK_TYPE_VIDEO_RAW; - else if (!strcmp (tplname, "text_sink")) - type = GST_PLAY_SINK_TYPE_TEXT; - else - goto unknown_template; - - pad = gst_play_sink_request_pad (psink, type); - return pad; - -unknown_template: - GST_WARNING_OBJECT (element, "Unknown pad template"); - return NULL; -} - -void -gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad) -{ - GstPad **res = NULL; - gboolean untarget = TRUE; - - GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad); - - GST_PLAY_SINK_LOCK (playsink); - if (pad == playsink->video_pad) { - res = &playsink->video_pad; - } else if (pad == playsink->audio_pad) { - res = &playsink->audio_pad; - } else if (pad == playsink->text_pad) { - res = &playsink->text_pad; - } else { - /* try to release the given pad anyway, these could be the FLUSHING pads. */ - res = &pad; - untarget = FALSE; - } - GST_PLAY_SINK_UNLOCK (playsink); - - if (*res) { - GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res); - gst_pad_set_active (*res, FALSE); - if (untarget) { - GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL); - } - GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res); - gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res); - *res = NULL; - } -} - -static void -gst_play_sink_release_request_pad (GstElement * element, GstPad * pad) -{ - GstPlaySink *psink = GST_PLAY_SINK (element); - - gst_play_sink_release_pad (psink, pad); -} - -static void -gst_play_sink_handle_message (GstBin * bin, GstMessage * message) -{ - GstPlaySink *playsink; - - playsink = GST_PLAY_SINK_CAST (bin); - - switch (GST_MESSAGE_TYPE (message)) { - case GST_MESSAGE_STEP_DONE: - { - GstFormat format; - guint64 amount; - gdouble rate; - gboolean flush, intermediate, eos; - guint64 duration; - - GST_INFO_OBJECT (playsink, "Handling step-done message"); - gst_message_parse_step_done (message, &format, &amount, &rate, &flush, - &intermediate, &duration, &eos); - - if (format == GST_FORMAT_BUFFERS) { - /* for the buffer format, we align the other streams */ - if (playsink->audiochain) { - GstEvent *event; - - event = - gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush, - intermediate); - - if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) { - GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink"); - } - } - } - GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message); - break; - } - default: - GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message); - break; - } -} - -/* Send an event to our sinks until one of them works; don't then send to the - * remaining sinks (unlike GstBin) - * Special case: If a text sink is set we need to send the event - * to them in case it's source is different from the a/v stream's source. - */ -static gboolean -gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event) -{ - gboolean res = TRUE; - - if (playsink->textchain && playsink->textchain->sink) { - gst_event_ref (event); - if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) { - GST_DEBUG_OBJECT (playsink, "Sent event succesfully to text sink"); - } else { - GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink"); - } - } - - if (playsink->videochain) { - gst_event_ref (event); - if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) { - GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink"); - goto done; - } - GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink"); - } - if (playsink->audiochain) { - gst_event_ref (event); - if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) { - GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink"); - goto done; - } - GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink"); - } - -done: - gst_event_unref (event); - return res; -} - -/* We only want to send the event to a single sink (overriding GstBin's - * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek - * events appropriately. So, this is a messy duplication of code. */ -static gboolean -gst_play_sink_send_event (GstElement * element, GstEvent * event) -{ - gboolean res = FALSE; - GstEventType event_type = GST_EVENT_TYPE (event); - GstPlaySink *playsink; - - playsink = GST_PLAY_SINK_CAST (element); - - switch (event_type) { - case GST_EVENT_SEEK: - GST_DEBUG_OBJECT (element, "Sending event to a sink"); - res = gst_play_sink_send_event_to_sink (playsink, event); - break; - case GST_EVENT_STEP: - { - GstFormat format; - guint64 amount; - gdouble rate; - gboolean flush, intermediate; - - gst_event_parse_step (event, &format, &amount, &rate, &flush, - &intermediate); - - if (format == GST_FORMAT_BUFFERS) { - /* for buffers, we will try to step video frames, for other formats we - * send the step to all sinks */ - res = gst_play_sink_send_event_to_sink (playsink, event); - } else { - res = - GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element, - event); - } - break; - } - default: - res = - GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element, - event); - break; - } - return res; -} - -static GstStateChangeReturn -gst_play_sink_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstStateChangeReturn bret; - - GstPlaySink *playsink; - - playsink = GST_PLAY_SINK (element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - playsink->need_async_start = TRUE; - /* we want to go async to PAUSED until we managed to configure and add the - * sinks */ - do_async_start (playsink); - ret = GST_STATE_CHANGE_ASYNC; - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - case GST_STATE_CHANGE_READY_TO_NULL: - if (playsink->audiochain && playsink->audiochain->sink_volume) { - /* remove our links to the mute and volume elements when they were - * provided by a sink */ - playsink->audiochain->volume = NULL; - playsink->audiochain->mute = NULL; - } - ret = GST_STATE_CHANGE_SUCCESS; - break; - default: - /* all other state changes return SUCCESS by default, this value can be - * overridden by the result of the children */ - ret = GST_STATE_CHANGE_SUCCESS; - break; - } - - /* do the state change of the children */ - bret = - GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element, - transition); - /* now look at the result of our children and adjust the return value */ - switch (bret) { - case GST_STATE_CHANGE_FAILURE: - /* failure, we stop */ - goto activate_failed; - case GST_STATE_CHANGE_NO_PREROLL: - /* some child returned NO_PREROLL. This is strange but we never know. We - * commit our async state change (if any) and return the NO_PREROLL */ - do_async_done (playsink); - ret = bret; - break; - case GST_STATE_CHANGE_ASYNC: - /* some child was async, return this */ - ret = bret; - break; - default: - /* return our previously configured return value */ - break; - } - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - /* FIXME Release audio device when we implement that */ - playsink->need_async_start = TRUE; - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - case GST_STATE_CHANGE_READY_TO_NULL: - /* remove sinks we added */ - if (playsink->videochain) { - activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); - add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); - } - if (playsink->audiochain) { - activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); - add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); - } - if (playsink->vischain) { - activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE); - add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE); - } - if (playsink->textchain) { - activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE); - add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE); - } - do_async_done (playsink); - break; - default: - break; - } - return ret; - - /* ERRORS */ -activate_failed: - { - GST_DEBUG_OBJECT (element, - "element failed to change states -- activation problem?"); - return GST_STATE_CHANGE_FAILURE; - } -} - -static void -gst_play_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * spec) -{ - GstPlaySink *playsink = GST_PLAY_SINK (object); - - switch (prop_id) { - case PROP_FLAGS: - gst_play_sink_set_flags (playsink, g_value_get_flags (value)); - break; - case PROP_VOLUME: - gst_play_sink_set_volume (playsink, g_value_get_double (value)); - break; - case PROP_MUTE: - gst_play_sink_set_mute (playsink, g_value_get_boolean (value)); - break; - case PROP_FONT_DESC: - gst_play_sink_set_font_desc (playsink, g_value_get_string (value)); - break; - case PROP_SUBTITLE_ENCODING: - gst_play_sink_set_subtitle_encoding (playsink, - g_value_get_string (value)); - break; - case PROP_VIS_PLUGIN: - gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec); - break; - } -} - -static void -gst_play_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * spec) -{ - GstPlaySink *playsink = GST_PLAY_SINK (object); - - switch (prop_id) { - case PROP_FLAGS: - g_value_set_flags (value, gst_play_sink_get_flags (playsink)); - break; - case PROP_VOLUME: - g_value_set_double (value, gst_play_sink_get_volume (playsink)); - break; - case PROP_MUTE: - g_value_set_boolean (value, gst_play_sink_get_mute (playsink)); - break; - case PROP_FONT_DESC: - g_value_take_string (value, gst_play_sink_get_font_desc (playsink)); - break; - case PROP_SUBTITLE_ENCODING: - g_value_take_string (value, - gst_play_sink_get_subtitle_encoding (playsink)); - break; - case PROP_VIS_PLUGIN: - g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec); - break; - } -} - - -gboolean -gst_play_sink_plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin"); - - return gst_element_register (plugin, "playsink", GST_RANK_NONE, - GST_TYPE_PLAY_SINK); -} |