[empathy/gnome-3-4] audio input: Switch to stream volumes



commit 9f1691878cc95951039eb2d7a47b6462569da299
Author: Sjoerd Simons <sjoerd luon net>
Date:   Mon May 21 11:55:25 2012 +0200

    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).

 src/empathy-audio-src.c |  214 ++++++++++++++++-------------------------------
 1 files changed, 72 insertions(+), 142 deletions(-)
---
diff --git a/src/empathy-audio-src.c b/src/empathy-audio-src.c
index 9a882c1..5a82979 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);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]