diff options
-rw-r--r-- | src/empathy-audio-src.c | 218 |
1 files changed, 179 insertions, 39 deletions
diff --git a/src/empathy-audio-src.c b/src/empathy-audio-src.c index 19af8877a..a91d3b286 100644 --- a/src/empathy-audio-src.c +++ b/src/empathy-audio-src.c @@ -23,6 +23,8 @@ #include <stdio.h> #include <stdlib.h> +#include <gst/interfaces/mixer.h> + #include <libempathy/empathy-utils.h> #include <libempathy-gtk/empathy-call-utils.h> @@ -57,7 +59,6 @@ struct _EmpathyGstAudioSrcPrivate { gboolean dispose_has_run; GstElement *src; - GstElement *volume; GstElement *level; EmpathyMicMonitor *mic_monitor; @@ -70,14 +71,67 @@ struct _EmpathyGstAudioSrcPrivate gdouble peak_level; gdouble rms_level; + gdouble volume; + /* the mixer track on src we follow and adjust */ + GstMixerTrack *track; + GMutex *lock; - guint idle_id; + guint level_idle_id; + guint volume_idle_id; }; #define EMPATHY_GST_AUDIO_SRC_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SRC, \ EmpathyGstAudioSrcPrivate)) +/* There is no predefined maximum channels by gstreamer, just pick 32, which is + * the same as the pulseaudio maximum */ +#define MAX_MIC_CHANNELS 32 + +static void +empathy_audio_src_set_hw_volume (EmpathyGstAudioSrc *self, gdouble volume) +{ + gint volumes[MAX_MIC_CHANNELS]; + int i; + + g_mutex_lock (self->priv->lock); + /* If there is no mixer available ignore the setting */ + if (self->priv->track == NULL) + goto out; + + for (i = 0; i < MAX_MIC_CHANNELS; i++) + volumes[i] = self->priv->track->max_volume * volume; + + gst_mixer_set_volume (GST_MIXER (self->priv->src), + self->priv->track, volumes); + +out: + g_mutex_unlock (self->priv->lock); + + self->priv->volume = volume; +} + +static gdouble +empathy_audio_src_get_hw_volume (EmpathyGstAudioSrc *self) +{ + gint volumes[MAX_MIC_CHANNELS]; + gdouble result = self->priv->volume; + + g_mutex_lock (self->priv->lock); + if (self->priv->track == NULL) + goto out; + + gst_mixer_get_volume (GST_MIXER (self->priv->src), + self->priv->track, volumes); + result = volumes[0]/(gdouble)self->priv->track->max_volume; + +out: + g_mutex_unlock (self->priv->lock); + + return result; +} + + gboolean empathy_audio_src_supports_changing_mic (EmpathyGstAudioSrc *self) { @@ -104,7 +158,6 @@ empathy_audio_src_get_mic_index (EmpathyGstAudioSrc *self) return audio_src_idx; } - static void empathy_audio_src_microphone_changed_cb (EmpathyMicMonitor *monitor, guint source_output_idx, @@ -178,6 +231,43 @@ empathy_audio_src_source_output_index_notify (GObject *object, source_output_idx, empathy_audio_src_get_current_mic_cb, self); } +static GstMixerTrack * +empathy_audio_src_get_track (GstElement *src) +{ + const GList *t; + GstMixerTrack *track = NULL; + + if (!gst_element_implements_interface (src, GST_TYPE_MIXER)) + { + g_warning ("No mixer interface implementation, can't control volume"); + return NULL; + } + + for (t = gst_mixer_list_tracks (GST_MIXER (src)); + t != NULL; t = g_list_next (t)) + { + GstMixerTrack *tr = t->data; + if (!tp_strdiff (tr->label, "Master")) + { + track = tr; + break; + } + } + + if (track == NULL) + { + g_warning ("No suitable track found"); + } + else if (track->num_channels > MAX_MIC_CHANNELS) + { + g_warning ("Microphones with more then %d channels not supported ", + MAX_MIC_CHANNELS); + track = NULL; + } + + return track; +} + static GstElement * create_src (void) { @@ -221,6 +311,7 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj) obj->priv = priv; priv->peak_level = -G_MAXDOUBLE; priv->lock = g_mutex_new (); + priv->volume = 1.0; priv->src = create_src (); if (priv->src == NULL) @@ -244,15 +335,9 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj) gst_bin_add (GST_BIN (obj), capsfilter); gst_element_link (priv->src, capsfilter); - priv->volume = gst_element_factory_make ("volume", NULL); - g_object_ref (priv->volume); - - gst_bin_add (GST_BIN (obj), priv->volume); - gst_element_link (capsfilter, priv->volume); - priv->level = gst_element_factory_make ("level", NULL); gst_bin_add (GST_BIN (obj), priv->level); - gst_element_link (priv->volume, priv->level); + gst_element_link (capsfilter, priv->level); src = gst_element_get_static_pad (priv->level, "src"); @@ -289,7 +374,7 @@ empathy_audio_src_set_property (GObject *object, switch (property_id) { case PROP_VOLUME: - empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (object), + empathy_audio_src_set_hw_volume (EMPATHY_GST_AUDIO_SRC (object), g_value_get_double (value)); break; default: @@ -307,8 +392,7 @@ empathy_audio_src_get_property (GObject *object, switch (property_id) { case PROP_VOLUME: - g_value_set_double (value, - empathy_audio_src_get_volume (self)); + g_value_set_double (value, priv->volume); break; case PROP_PEAK_LEVEL: g_mutex_lock (priv->lock); @@ -396,10 +480,13 @@ empathy_audio_src_dispose (GObject *object) priv->dispose_has_run = TRUE; - if (priv->idle_id != 0) - g_source_remove (priv->idle_id); + if (priv->level_idle_id != 0) + g_source_remove (priv->level_idle_id); + priv->level_idle_id = 0; - priv->idle_id = 0; + if (priv->volume_idle_id != 0) + g_source_remove (priv->volume_idle_id); + priv->volume_idle_id = 0; tp_clear_object (&priv->mic_monitor); @@ -431,13 +518,35 @@ empathy_audio_src_levels_updated (gpointer user_data) g_signal_emit (self, signals[PEAK_LEVEL_CHANGED], 0, priv->peak_level); g_signal_emit (self, signals[RMS_LEVEL_CHANGED], 0, priv->rms_level); - priv->idle_id = 0; + priv->level_idle_id = 0; g_mutex_unlock (priv->lock); return FALSE; } +static gboolean +empathy_audio_src_volume_changed (gpointer user_data) +{ + EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data); + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self); + gdouble volume; + + g_mutex_lock (priv->lock); + priv->volume_idle_id = 0; + g_mutex_unlock (priv->lock); + + volume = empathy_audio_src_get_hw_volume (self); + + if (volume != priv->volume) + { + priv->volume = volume; + g_object_notify (G_OBJECT (self), "volume"); + } + + return FALSE; +} + static void empathy_audio_src_handle_message (GstBin *bin, GstMessage *message) { @@ -490,11 +599,60 @@ empathy_audio_src_handle_message (GstBin *bin, GstMessage *message) priv->peak_level = peak; priv->rms_level = rms; - if (priv->idle_id == 0) - priv->idle_id = g_idle_add (empathy_audio_src_levels_updated, self); + if (priv->level_idle_id == 0) + priv->level_idle_id = g_idle_add ( + empathy_audio_src_levels_updated, self); g_mutex_unlock (priv->lock); } + else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT && + GST_MESSAGE_SRC (message) == GST_OBJECT (priv->src)) + { + /* Listen for volume changes on the src element */ + if (gst_mixer_message_get_type (message) == + GST_MIXER_MESSAGE_VOLUME_CHANGED) + { + GstMixerTrack *track; + + gst_mixer_message_parse_volume_changed (message, &track, + NULL, NULL); + + g_mutex_lock (priv->lock); + + if (track == priv->track && priv->volume_idle_id == 0) + priv->volume_idle_id = g_idle_add ( + empathy_audio_src_volume_changed, self); + g_mutex_unlock (priv->lock); + } + } + else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STATE_CHANGED && + GST_MESSAGE_SRC (message) == GST_OBJECT (priv->src)) + { + GstState old, new; + + gst_message_parse_state_changed (message, &old, &new, NULL); + + /* GstMixer is only available in state >= READY, so only start + * controlling the source element when going to ready state and stop + * doing so when going below ready. Furthermore once we have mixer read + * the current volume level from it and remove the settings done by + * Empathy. We want to pick up the level pulseaudio saved */ + if (old == GST_STATE_NULL && new == GST_STATE_READY) + { + g_mutex_lock (priv->lock); + priv->track = empathy_audio_src_get_track (priv->src); + if (priv->track != NULL) + priv->volume_idle_id = g_idle_add ( + empathy_audio_src_volume_changed, self); + g_mutex_unlock (priv->lock); + } + else if (old == GST_STATE_READY && new == GST_STATE_NULL) + { + g_mutex_lock (priv->lock); + priv->track = NULL; + g_mutex_unlock (priv->lock); + } + } out: GST_BIN_CLASS (empathy_audio_src_parent_class)->handle_message (bin, @@ -526,31 +684,13 @@ empathy_audio_src_set_echo_cancel (EmpathyGstAudioSrc *src, void empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume) { - EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src); - GParamSpec *pspec; - GParamSpecDouble *pspec_double; - - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (priv->volume), - "volume"); - - g_assert (pspec != NULL); - - pspec_double = G_PARAM_SPEC_DOUBLE (pspec); - - volume = CLAMP (volume, pspec_double->minimum, pspec_double->maximum); - - g_object_set (G_OBJECT (priv->volume), "volume", volume, NULL); + g_object_set (src, "volume", volume, NULL); } gdouble empathy_audio_src_get_volume (EmpathyGstAudioSrc *src) { - EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src); - gdouble volume; - - g_object_get (G_OBJECT (priv->volume), "volume", &volume, NULL); - - return volume; + return src->priv->volume; } guint |