aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSjoerd Simons <sjoerd@luon.net>2012-05-21 17:55:25 +0800
committerSjoerd Simons <sjoerd@luon.net>2012-05-21 19:54:00 +0800
commit9f1691878cc95951039eb2d7a47b6462569da299 (patch)
tree7f55e6f36cce45a7a5fd8da0cf567c3095966ca6
parent246b14f2722e6aac0c9744e49edd70594846f004 (diff)
downloadgsoc2013-empathy-9f1691878cc95951039eb2d7a47b6462569da299.tar
gsoc2013-empathy-9f1691878cc95951039eb2d7a47b6462569da299.tar.gz
gsoc2013-empathy-9f1691878cc95951039eb2d7a47b6462569da299.tar.bz2
gsoc2013-empathy-9f1691878cc95951039eb2d7a47b6462569da299.tar.lz
gsoc2013-empathy-9f1691878cc95951039eb2d7a47b6462569da299.tar.xz
gsoc2013-empathy-9f1691878cc95951039eb2d7a47b6462569da299.tar.zst
gsoc2013-empathy-9f1691878cc95951039eb2d7a47b6462569da299.zip
audio input: Switch to stream volumes
Newer gstreamer & pulseaudio support the stream volume interface for input as well. Prefer this over using the mixer interface as it's both simpler and actually does as intended. Besides that the mixer interface is buggy and seems to not correctly adjust the current input device if the source was switch to a non-default input. As an extra put in a volume element to locally enforce the current mute state. This ensure that whatever happens, if the UI says mute, the stream is guaranteed to be muted. This prevents awkward situations if the source element doesn't support stream volumes or the notification is buggy (like with current pulsesrc in releases).
-rw-r--r--src/empathy-audio-src.c214
1 files changed, 72 insertions, 142 deletions
diff --git a/src/empathy-audio-src.c b/src/empathy-audio-src.c
index 9a882c146..5a82979aa 100644
--- a/src/empathy-audio-src.c
+++ b/src/empathy-audio-src.c
@@ -23,7 +23,7 @@
#include <stdio.h>
#include <stdlib.h>
-#include <gst/interfaces/mixer.h>
+#include <gst/interfaces/streamvolume.h>
#include <libempathy/empathy-utils.h>
#include <libempathy-gtk/empathy-call-utils.h>
@@ -61,6 +61,7 @@ struct _EmpathyGstAudioSrcPrivate
gboolean dispose_has_run;
GstElement *src;
GstElement *level;
+ GstElement *volume_element;
EmpathyMicMonitor *mic_monitor;
@@ -74,8 +75,6 @@ struct _EmpathyGstAudioSrcPrivate
gdouble volume;
gboolean mute;
- /* the mixer track on src we follow and adjust */
- GstMixerTrack *track;
GMutex *lock;
guint level_idle_id;
@@ -86,37 +85,34 @@ struct _EmpathyGstAudioSrcPrivate
(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 gboolean
+empathy_audio_src_volume_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data);
static void
empathy_audio_set_hw_mute (EmpathyGstAudioSrc *self, gboolean mute)
{
- g_mutex_lock (self->priv->lock);
- /* If there is no mixer available ignore the setting */
- if (self->priv->track == NULL)
- goto out;
+ if (mute == self->priv->mute)
+ return;
- gst_mixer_set_mute (GST_MIXER (self->priv->src), self->priv->track, mute);
+ g_object_set (self->priv->src, "mute", mute, NULL);
+
+ /* Belt and braces: If for some reason the underlying src doesn't mute
+ * correctly or doesn't update us when it unmutes correctly enforce it using
+ * our own volume element. Our UI can in no circumstances be made to think
+ * the input is muted while it's not */
+ g_object_set (self->priv->volume_element, "mute", mute, NULL);
-out:
- g_mutex_unlock (self->priv->lock);
self->priv->mute = mute;
}
static gboolean
empathy_audio_src_get_hw_mute (EmpathyGstAudioSrc *self)
{
- gboolean result = self->priv->mute;
-
- g_mutex_lock (self->priv->lock);
- if (self->priv->track == NULL)
- goto out;
-
- result = GST_MIXER_TRACK_HAS_FLAG (self->priv->track, GST_MIXER_TRACK_MUTE);
-out:
- g_mutex_unlock (self->priv->lock);
+ gboolean result;
+ g_object_get (self->priv->src, "mute", &result, NULL);
return result;
}
@@ -125,42 +121,18 @@ 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);
+ if (volume == self->priv->volume)
+ return;
+ g_object_set (self->priv->src, "volume", volume, NULL);
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);
+ gdouble result;
+ g_object_get (self->priv->src, "volume", &result, NULL);
return result;
}
@@ -265,43 +237,6 @@ 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)
{
@@ -354,6 +289,31 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj)
if (priv->src == NULL)
return;
+ if (GST_IS_STREAM_VOLUME (priv->src))
+ {
+ gdouble volume;
+ gboolean mute;
+ /* We can't do a bidirection bind as the ::notify comes from another
+ * thread, for other bits of empathy it's most simpler if it comes from
+ * the main thread */
+ g_object_bind_property (obj, "volume", priv->src, "volume",
+ G_BINDING_DEFAULT);
+ g_object_bind_property (obj, "mute", priv->src, "mute",
+ G_BINDING_DEFAULT);
+
+ /* sync and callback for bouncing */
+ g_object_get (priv->src, "volume", &volume, NULL);
+ g_object_set (obj, "volume", volume, NULL);
+
+ g_object_get (priv->src, "mute", &mute, NULL);
+ g_object_set (obj, "mute", mute, NULL);
+
+ g_signal_connect (priv->src, "notify::volume",
+ G_CALLBACK (empathy_audio_src_volume_changed), obj);
+ g_signal_connect (priv->src, "notify::mute",
+ G_CALLBACK (empathy_audio_src_volume_changed), obj);
+ }
+
gst_bin_add (GST_BIN (obj), priv->src);
/* Explicitly state what format we want from pulsesrc. This pushes resampling
@@ -372,9 +332,13 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj)
gst_bin_add (GST_BIN (obj), capsfilter);
gst_element_link (priv->src, capsfilter);
+ priv->volume_element = gst_element_factory_make ("volume", NULL);
+ gst_bin_add (GST_BIN (obj), priv->volume_element);
+ gst_element_link (capsfilter, priv->volume_element);
+
priv->level = gst_element_factory_make ("level", NULL);
gst_bin_add (GST_BIN (obj), priv->level);
- gst_element_link (capsfilter, priv->level);
+ gst_element_link (priv->volume_element, priv->level);
src = gst_element_get_static_pad (priv->level, "src");
@@ -575,7 +539,7 @@ empathy_audio_src_levels_updated (gpointer user_data)
}
static gboolean
-empathy_audio_src_volume_changed (gpointer user_data)
+empathy_audio_src_volume_changed_idle (gpointer user_data)
{
EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
@@ -598,12 +562,30 @@ empathy_audio_src_volume_changed (gpointer user_data)
if (mute != priv->mute)
{
priv->mute = mute;
+ /* hw mute changed, follow with own volume */
+ g_object_set (self->priv->volume_element, "mute", mute, NULL);
g_object_notify (G_OBJECT (self), "mute");
}
return FALSE;
}
+static gboolean
+empathy_audio_src_volume_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
+
+ g_mutex_lock (self->priv->lock);
+ if (self->priv->volume_idle_id == 0)
+ self->priv->volume_idle_id = g_idle_add (
+ empathy_audio_src_volume_changed_idle, self);
+ g_mutex_unlock (self->priv->lock);
+
+ return FALSE;
+}
+
static void
empathy_audio_src_handle_message (GstBin *bin, GstMessage *message)
{
@@ -662,58 +644,6 @@ empathy_audio_src_handle_message (GstBin *bin, GstMessage *message)
g_mutex_unlock (priv->lock);
}
- else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT &&
- GST_MESSAGE_SRC (message) == GST_OBJECT (priv->src))
- {
- GstMixerTrack *track = NULL;
-
- /* Listen for mute or volume changes on the src element */
- if (gst_mixer_message_get_type (message) ==
- GST_MIXER_MESSAGE_VOLUME_CHANGED)
- gst_mixer_message_parse_volume_changed (message, &track,
- NULL, NULL);
-
- if (gst_mixer_message_get_type (message) ==
- GST_MIXER_MESSAGE_MUTE_TOGGLED)
- gst_mixer_message_parse_mute_toggled (message, &track, NULL);
-
- g_mutex_lock (priv->lock);
-
- if (track != NULL && 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,
message);