From e9fb5d29ff44ae35411b02c3715ae9650b61dd25 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Fri, 11 Mar 2011 19:01:17 +0000 Subject: Implement request pads for multiple sinks Instead of using an live-adder we're moving to using multiple pulseaudio sinks as pulse can do the right thing (tm). To make this easy for the call window have one bin that handles all the juggling of audiosinks and audio conversion --- src/empathy-audio-sink.c | 274 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 228 insertions(+), 46 deletions(-) (limited to 'src/empathy-audio-sink.c') diff --git a/src/empathy-audio-sink.c b/src/empathy-audio-sink.c index 1d2169593..31fffff10 100644 --- a/src/empathy-audio-sink.c +++ b/src/empathy-audio-sink.c @@ -22,6 +22,7 @@ #include #include +#include #include #include "empathy-audio-sink.h" @@ -38,20 +39,58 @@ enum static guint signals[LAST_SIGNAL] = {0}; #endif +typedef struct { + GstPad *pad; + GstElement *bin; + GstElement *volume; + GstElement *sink; +} AudioBin; + +static AudioBin * +audio_bin_new (GstPad *pad, + GstElement *bin, + GstElement *volume, + GstElement *sink) +{ + AudioBin *result = g_slice_new0 (AudioBin); + + result->pad = pad; + result->bin = bin; + result->volume = gst_object_ref (volume); + result->sink = sink; + + return result; +} + +static void +audio_bin_free (AudioBin *bin) +{ + gst_object_unref (bin->volume); + g_slice_free (AudioBin, bin); +} + + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE( + "sink%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ( GST_AUDIO_INT_PAD_TEMPLATE_CAPS " ; " + GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS) +); enum { PROP_VOLUME = 1, }; -/* private structure */ -typedef struct _EmpathyGstAudioSinkPrivate EmpathyGstAudioSinkPrivate; - struct _EmpathyGstAudioSinkPrivate { gboolean dispose_has_run; - GstElement *sink; - GstElement *volume; FsElementAddedNotifier *notifier; + + gdouble volume; + + /* Pad -> *owned* subbin hash */ + GHashTable *audio_bins; }; #define EMPATHY_GST_AUDIO_SINK_GET_PRIVATE(o) \ @@ -66,54 +105,68 @@ empathy_audio_sink_element_added_cb (FsElementAddedNotifier *notifier, if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume")) { - gdouble volume; - - volume = empathy_audio_sink_get_volume (self); - empathy_audio_sink_set_volume (self, 1.0); - - if (priv->volume != NULL) - g_object_unref (priv->volume); - priv->volume = g_object_ref (element); - - if (volume != 1.0) - empathy_audio_sink_set_volume (self, volume); + /* An element was added with a volume property, lets find its subbin and + * update the volume in it */ + GHashTableIter iter; + AudioBin *audio_bin = NULL; + gpointer value; + + g_hash_table_iter_init (&iter, priv->audio_bins); + + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + AudioBin *b = value; + + if (gst_object_has_ancestor (GST_OBJECT (element), + GST_OBJECT (b->bin))) + { + audio_bin = b; + break; + } + } + + if (audio_bin == NULL) + { + g_warning ("Element added that doesn't belong to us ?"); + return; + } + + /* Set the old volume to 1 and the new volume to the volume */ + g_object_set (audio_bin->volume, "volume", 1.0, NULL); + gst_object_unref (audio_bin->volume); + + audio_bin->volume = gst_object_ref (element); + g_object_set (audio_bin->volume, "volume", self->priv->volume, NULL); } } static void -empathy_audio_sink_init (EmpathyGstAudioSink *obj) +empathy_audio_sink_init (EmpathyGstAudioSink *self) { - EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (obj); - GstElement *resample; - GstPad *ghost, *sink; - - priv->notifier = fs_element_added_notifier_new (); - g_signal_connect (priv->notifier, "element-added", - G_CALLBACK (empathy_audio_sink_element_added_cb), obj); - - resample = gst_element_factory_make ("audioresample", NULL); - - priv->volume = gst_element_factory_make ("volume", NULL); - g_object_ref (priv->volume); - - priv->sink = gst_element_factory_make ("gconfaudiosink", NULL); - - fs_element_added_notifier_add (priv->notifier, GST_BIN (priv->sink)); + EmpathyGstAudioSinkPrivate *priv; - gst_bin_add_many (GST_BIN (obj), resample, priv->volume, priv->sink, NULL); - gst_element_link_many (resample, priv->volume, priv->sink, NULL); + priv = self->priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self); - sink = gst_element_get_static_pad (resample, "sink"); + priv->volume = 1.0; - ghost = gst_ghost_pad_new ("sink", sink); - gst_element_add_pad (GST_ELEMENT (obj), ghost); + priv->audio_bins = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) audio_bin_free); - gst_object_unref (G_OBJECT (sink)); + priv->notifier = fs_element_added_notifier_new (); + g_signal_connect (priv->notifier, "element-added", + G_CALLBACK (empathy_audio_sink_element_added_cb), self); } static void empathy_audio_sink_dispose (GObject *object); static void empathy_audio_sink_finalize (GObject *object); +static GstPad * empathy_audio_sink_request_new_pad (GstElement *self, + GstPadTemplate *templ, + const gchar* name); + +static void empathy_audio_sink_release_pad (GstElement *self, + GstPad *pad); + static void empathy_audio_sink_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) @@ -149,8 +202,13 @@ empathy_audio_sink_class_init (EmpathyGstAudioSinkClass *empathy_audio_sink_class) { GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_sink_class); + GstElementClass *element_class = + GST_ELEMENT_CLASS (empathy_audio_sink_class); GParamSpec *param_spec; + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + g_type_class_add_private (empathy_audio_sink_class, sizeof (EmpathyGstAudioSinkPrivate)); @@ -160,6 +218,9 @@ empathy_audio_sink_class_init (EmpathyGstAudioSinkClass object_class->set_property = empathy_audio_sink_set_property; object_class->get_property = empathy_audio_sink_get_property; + element_class->request_new_pad = empathy_audio_sink_request_new_pad; + element_class->release_pad = empathy_audio_sink_release_pad; + param_spec = g_param_spec_double ("volume", "Volume", "volume control", 0.0, 5.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); @@ -181,9 +242,9 @@ empathy_audio_sink_dispose (GObject *object) g_object_unref (priv->notifier); priv->notifier = NULL; - if (priv->volume != NULL) - g_object_unref (priv->volume); - priv->volume = NULL; + if (priv->audio_bins != NULL) + g_hash_table_unref (priv->audio_bins); + priv->audio_bins = NULL; if (G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose) G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose (object); @@ -219,17 +280,138 @@ void empathy_audio_sink_set_volume (EmpathyGstAudioSink *sink, gdouble volume) { EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink); + GHashTableIter iter; + gpointer value; + + priv->volume = volume; + g_hash_table_iter_init (&iter, priv->audio_bins); - g_object_set (G_OBJECT (priv->volume), "volume", volume, NULL); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + AudioBin *b = value; + g_object_set (b->volume, "volume", volume, NULL); + } } gdouble empathy_audio_sink_get_volume (EmpathyGstAudioSink *sink) { EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink); - gdouble volume; + return priv->volume; +} + +static GstPad * +empathy_audio_sink_request_new_pad (GstElement *element, + GstPadTemplate *templ, + const gchar* name) +{ + EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element); + GstElement *bin, *sink, *volume, *resample, *audioconvert0, *audioconvert1; + GstPad *pad = NULL; + GstPad *subpad, *filterpad; + AudioBin *audiobin; + + bin = gst_bin_new (NULL); + + audioconvert0 = gst_element_factory_make ("audioconvert", NULL); + if (audioconvert0 == NULL) + goto error; + + gst_bin_add (GST_BIN (bin), audioconvert0); + + resample = gst_element_factory_make ("audioresample", NULL); + if (resample == NULL) + goto error; + + gst_bin_add (GST_BIN (bin), resample); + + audioconvert1 = gst_element_factory_make ("audioconvert", NULL); + if (audioconvert1 == NULL) + goto error; + + gst_bin_add (GST_BIN (bin), audioconvert1); + + volume = gst_element_factory_make ("volume", NULL); + if (volume == NULL) + goto error; + + gst_bin_add (GST_BIN (bin), volume); + + sink = gst_element_factory_make ("gconfaudiosink", NULL); + if (sink == NULL) + goto error; + + gst_bin_add (GST_BIN (bin), sink); + fs_element_added_notifier_add (self->priv->notifier, GST_BIN (sink)); + + if (!gst_element_link_many (audioconvert0, resample, audioconvert1, + volume, sink, NULL)) + goto error; + + filterpad = gst_element_get_static_pad (audioconvert0, "sink"); + + if (filterpad == NULL) + goto error; + + subpad = gst_ghost_pad_new ("sink", filterpad); + if (!gst_element_add_pad (GST_ELEMENT (bin), subpad)) + goto error; + + gst_bin_add (GST_BIN (self), bin); + + /* Add elemnt into the hash table before syncing state so we know about it + * when elements are added */ + pad = gst_ghost_pad_new (name, subpad); + audiobin = audio_bin_new (pad, bin, volume, sink); + + g_hash_table_insert (self->priv->audio_bins, pad, audiobin); + + if (!gst_element_sync_state_with_parent (bin)) + goto error; + + if (!gst_pad_set_active (pad, TRUE)) + goto error; + + if (!gst_element_add_pad (GST_ELEMENT (self), pad)) + goto error; + + + return pad; + +error: + if (pad != NULL) + { + g_hash_table_remove (self->priv->audio_bins, pad); + gst_object_unref (pad); + } + + gst_object_unref (bin); + g_warning ("Failed to create output subpipeline"); + return NULL; +} + +static void +empathy_audio_sink_release_pad (GstElement *element, + GstPad *pad) +{ + EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element); + AudioBin *abin; + + abin = g_hash_table_lookup (self->priv->audio_bins, pad); + g_hash_table_steal (self->priv->audio_bins, pad); + + if (abin == NULL) + { + g_warning ("Releasing a pad that doesn't belong to us ?"); + return; + } + + gst_pad_set_active (pad, FALSE); + gst_element_remove_pad (element, pad); - g_object_get (G_OBJECT (priv->volume), "volume", &volume, NULL); + gst_element_set_locked_state (abin->bin, TRUE); + gst_element_set_state (abin->bin, GST_STATE_NULL); + gst_bin_remove (GST_BIN (self), abin->bin); - return volume; + audio_bin_free (abin); } -- cgit v1.2.3