]> git.0d.be Git - empathy.git/blobdiff - src/empathy-audio-sink.c
Reorder header inclusions accordingly to the Telepathy coding style
[empathy.git] / src / empathy-audio-sink.c
index c410d7a306d34a874d578916008ba03058348a1d..985a1fa36d83d8f825277b1676dc543601e40cd6 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#include "config.h"
+#include "empathy-audio-sink.h"
 
-#include <stdio.h>
-#include <stdlib.h>
+#ifdef HAVE_GST1
+#include <gst/audio/streamvolume.h>
+#else
+#include <gst/interfaces/streamvolume.h>
+#endif
 
-#include <gst/audio/audio.h>
-#include <gst/farsight/fs-element-added-notifier.h>
-
-#include "empathy-audio-sink.h"
+#include "empathy-audio-utils.h"
 
+#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
+#include "empathy-debug.h"
 
 G_DEFINE_TYPE(EmpathyGstAudioSink, empathy_audio_sink, GST_TYPE_BIN)
 
@@ -39,43 +43,17 @@ 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,
+#ifdef HAVE_GST1
+    GST_STATIC_CAPS ( "audio/x-raw" )
+#else
     GST_STATIC_CAPS ( GST_AUDIO_INT_PAD_TEMPLATE_CAPS " ; "
         GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
+#endif
 );
 
 enum {
@@ -84,92 +62,35 @@ enum {
 
 struct _EmpathyGstAudioSinkPrivate
 {
-  gboolean dispose_has_run;
-  FsElementAddedNotifier *notifier;
-
+  GstElement *sink;
+  gboolean echo_cancel;
   gdouble volume;
-
-  /* Pad -> *owned* subbin hash */
-  GHashTable *audio_bins;
-
-  /* Mutex to hold while change the hash table */
-  GMutex *audio_bins_lock;
+  gint volume_idle_id;
+  GMutex volume_mutex;
 };
 
 #define EMPATHY_GST_AUDIO_SINK_GET_PRIVATE(o) \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SINK, \
   EmpathyGstAudioSinkPrivate))
 
-static void
-empathy_audio_sink_element_added_cb (FsElementAddedNotifier *notifier,
-  GstBin *bin, GstElement *element, EmpathyGstAudioSink *self)
-{
-  EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
-
-  if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), "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_mutex_lock (self->priv->audio_bins_lock);
-      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);
-      g_mutex_unlock (self->priv->audio_bins_lock);
-    }
-}
-
 static void
 empathy_audio_sink_init (EmpathyGstAudioSink *self)
 {
-  EmpathyGstAudioSinkPrivate *priv;
-
-  priv = self->priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
-
-  priv->volume = 1.0;
-
-  priv->audio_bins = g_hash_table_new_full (g_direct_hash, g_direct_equal,
-    NULL, (GDestroyNotify) audio_bin_free);
-
-  priv->audio_bins_lock = g_mutex_new ();
-
-  priv->notifier = fs_element_added_notifier_new ();
-  g_signal_connect (priv->notifier, "element-added",
-    G_CALLBACK (empathy_audio_sink_element_added_cb), self);
+  self->priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
+  self->priv->echo_cancel = TRUE;
+  g_mutex_init (&self->priv->volume_mutex);
 }
 
-static void empathy_audio_sink_dispose (GObject *object);
-static void empathy_audio_sink_finalize (GObject *object);
-
+#ifdef HAVE_GST1
+static GstPad * empathy_audio_sink_request_new_pad (GstElement *self,
+  GstPadTemplate *templ,
+  const gchar* name,
+  const GstCaps *caps);
+#else
 static GstPad * empathy_audio_sink_request_new_pad (GstElement *self,
   GstPadTemplate *templ,
   const gchar* name);
+#endif
 
 static void empathy_audio_sink_release_pad (GstElement *self,
   GstPad *pad);
@@ -178,11 +99,13 @@ static void
 empathy_audio_sink_set_property (GObject *object,
   guint property_id, const GValue *value, GParamSpec *pspec)
 {
+  EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
   switch (property_id)
     {
       case PROP_VOLUME:
-        empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (object),
-          g_value_get_double (value));
+        g_mutex_lock (&self->priv->volume_mutex);
+        self->priv->volume = g_value_get_double (value);
+        g_mutex_unlock (&self->priv->volume_mutex);
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -193,17 +116,34 @@ static void
 empathy_audio_sink_get_property (GObject *object,
   guint property_id, GValue *value, GParamSpec *pspec)
 {
+  EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
   switch (property_id)
     {
       case PROP_VOLUME:
-        g_value_set_double (value,
-          empathy_audio_sink_get_volume (EMPATHY_GST_AUDIO_SINK (object)));
+        g_value_set_double (value, self->priv->volume);
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
 }
 
+static void
+empathy_audio_sink_dispose (GObject *object)
+{
+  EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
+  EmpathyGstAudioSinkPrivate *priv = self->priv;
+
+  if (priv->volume_idle_id != 0)
+    g_source_remove (priv->volume_idle_id);
+  priv->volume_idle_id = 0;
+
+  g_mutex_clear (&self->priv->volume_mutex);
+
+  /* release any references held by the object here */
+  if (G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose)
+    G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose (object);
+}
+
 static void
 empathy_audio_sink_class_init (EmpathyGstAudioSinkClass
   *empathy_audio_sink_class)
@@ -219,11 +159,9 @@ empathy_audio_sink_class_init (EmpathyGstAudioSinkClass
   g_type_class_add_private (empathy_audio_sink_class,
     sizeof (EmpathyGstAudioSinkPrivate));
 
-  object_class->dispose = empathy_audio_sink_dispose;
-  object_class->finalize = empathy_audio_sink_finalize;
-
   object_class->set_property = empathy_audio_sink_set_property;
   object_class->get_property = empathy_audio_sink_get_property;
+  object_class->dispose = empathy_audio_sink_dispose;
 
   element_class->request_new_pad = empathy_audio_sink_request_new_pad;
   element_class->release_pad = empathy_audio_sink_release_pad;
@@ -234,45 +172,6 @@ empathy_audio_sink_class_init (EmpathyGstAudioSinkClass
   g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
 }
 
-void
-empathy_audio_sink_dispose (GObject *object)
-{
-  EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
-  EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
-
-  if (priv->dispose_has_run)
-    return;
-
-  priv->dispose_has_run = TRUE;
-
-  if (priv->notifier != NULL)
-    g_object_unref (priv->notifier);
-  priv->notifier = NULL;
-
-  if (priv->audio_bins != NULL)
-    g_hash_table_unref (priv->audio_bins);
-  priv->audio_bins = NULL;
-
-  if (priv->audio_bins_lock != NULL)
-    g_mutex_free (priv->audio_bins_lock);
-  priv->audio_bins_lock = NULL;
-
-  if (G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose)
-    G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose (object);
-}
-
-void
-empathy_audio_sink_finalize (GObject *object)
-{
-  //EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
-  //EmpathyGstAudioSinkPrivate *priv =
-  //  EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
-
-  /* free any data held directly by the object here */
-
-  G_OBJECT_CLASS (empathy_audio_sink_parent_class)->finalize (object);
-}
-
 GstElement *
 empathy_audio_sink_new (void)
 {
@@ -289,42 +188,136 @@ empathy_audio_sink_new (void)
 
 void
 empathy_audio_sink_set_volume (EmpathyGstAudioSink *sink, gdouble volume)
+{
+  g_object_set (sink, "volume", volume, NULL);
+}
+
+gdouble
+empathy_audio_sink_get_volume (EmpathyGstAudioSink *sink)
 {
   EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink);
-  GHashTableIter iter;
-  gpointer value;
 
-  priv->volume = volume;
+  return priv->volume;
+}
 
-  g_mutex_lock (priv->audio_bins_lock);
+static GstElement *
+create_sink (EmpathyGstAudioSink *self)
+{
+  GstElement *sink;
+  const gchar *description;
 
-  g_hash_table_iter_init (&iter, priv->audio_bins);
-  while (g_hash_table_iter_next (&iter, NULL, &value))
+  description = g_getenv ("EMPATHY_AUDIO_SINK");
+
+  if (description != NULL)
     {
-      AudioBin *b = value;
-      g_object_set (b->volume, "volume", volume, NULL);
+      GError *error = NULL;
+
+      sink = gst_parse_bin_from_description (description, TRUE, &error);
+      if (sink == NULL)
+        {
+          DEBUG ("Failed to create bin %s: %s", description, error->message);
+          g_error_free (error);
+        }
+
+      return sink;
     }
 
-  g_mutex_unlock (priv->audio_bins_lock);
+  /* Use pulsesink as default */
+  sink = gst_element_factory_make ("pulsesink", NULL);
+  if (sink == NULL)
+    return NULL;
+
+  empathy_audio_set_stream_properties (sink, self->priv->echo_cancel);
+
+  /* Set latency (buffering on the PulseAudio side) of 40ms and transfer data
+   * in 10ms chunks */
+  g_object_set (sink,
+      "buffer-time", (gint64) 40000,
+      "latency-time", (gint64) 10000,
+      NULL);
+
+  return sink;
 }
 
-gdouble
-empathy_audio_sink_get_volume (EmpathyGstAudioSink *sink)
+static gboolean
+empathy_audio_sink_volume_idle_updated (gpointer user_data)
 {
-  EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink);
-  return priv->volume;
+  EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (user_data);
+
+  g_mutex_lock (&self->priv->volume_mutex);
+  self->priv->volume_idle_id = 0;
+  g_mutex_unlock (&self->priv->volume_mutex);
+
+  g_object_notify (G_OBJECT (self), "volume");
+
+  return FALSE;
 }
 
+static void
+empathy_audio_sink_volume_updated (GObject *object,
+  GParamSpec *pspec,
+  gpointer user_data)
+{
+  EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (user_data);
+  gdouble volume;
+
+  g_mutex_lock (&self->priv->volume_mutex);
+
+  g_object_get (object, "volume", &volume, NULL);
+  if (self->priv->volume == volume)
+    goto out;
+
+  self->priv->volume = volume;
+  if (self->priv->volume_idle_id == 0)
+    self->priv->volume_idle_id = g_idle_add (
+      empathy_audio_sink_volume_idle_updated, self);
+
+out:
+  g_mutex_unlock (&self->priv->volume_mutex);
+}
+
+static gboolean
+empathy_audio_sink_volume_idle_setup (gpointer user_data)
+{
+  EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (user_data);
+  gdouble volume;
+
+  g_mutex_lock (&self->priv->volume_mutex);
+  self->priv->volume_idle_id = 0;
+  g_mutex_unlock (&self->priv->volume_mutex);
+
+  /* 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 (self, "volume", self->priv->sink, "volume",
+    G_BINDING_DEFAULT);
+
+  /* sync and callback for bouncing */
+  g_object_get (self->priv->sink, "volume", &volume, NULL);
+  g_object_set (self, "volume", volume, NULL);
+  g_signal_connect (self->priv->sink, "notify::volume",
+    G_CALLBACK (empathy_audio_sink_volume_updated), self);
+
+  return FALSE;
+}
+
+#ifdef HAVE_GST1
+static GstPad *
+empathy_audio_sink_request_new_pad (GstElement *element,
+  GstPadTemplate *templ,
+  const gchar* name,
+  const GstCaps *caps)
+#else
 static GstPad *
 empathy_audio_sink_request_new_pad (GstElement *element,
   GstPadTemplate *templ,
   const gchar* name)
+#endif
 {
   EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element);
-  GstElement *bin, *sink, *volume, *resample, *audioconvert0, *audioconvert1;
+  GstElement *bin, *resample, *audioconvert0, *audioconvert1;
   GstPad *pad = NULL;
   GstPad *subpad, *filterpad;
-  AudioBin *audiobin;
 
   bin = gst_bin_new (NULL);
 
@@ -346,21 +339,30 @@ empathy_audio_sink_request_new_pad (GstElement *element,
 
   gst_bin_add (GST_BIN (bin), audioconvert1);
 
-  volume = gst_element_factory_make ("volume", NULL);
-  if (volume == NULL)
+  self->priv->sink = create_sink (self);
+  if (self->priv->sink == NULL)
     goto error;
 
-  gst_bin_add (GST_BIN (bin), volume);
+  if (GST_IS_STREAM_VOLUME (self->priv->sink))
+    {
+      g_mutex_lock (&self->priv->volume_mutex);
+      if (self->priv->volume_idle_id == 0)
+        self->priv->volume_idle_id = g_idle_add (
+          empathy_audio_sink_volume_idle_setup, self);
+      g_mutex_unlock (&self->priv->volume_mutex);
+    }
+  else
+    {
+      gchar *n = gst_element_get_name (self->priv->sink);
 
-  sink = gst_element_factory_make ("gconfaudiosink", NULL);
-  if (sink == NULL)
-    goto error;
+      DEBUG ("Element %s doesn't support volume", n);
+      g_free (n);
+    }
 
-  gst_bin_add (GST_BIN (bin), sink);
-  fs_element_added_notifier_add (self->priv->notifier, GST_BIN (sink));
+  gst_bin_add (GST_BIN (bin), self->priv->sink);
 
   if (!gst_element_link_many (audioconvert0, resample, audioconvert1,
-      volume, sink, NULL))
+      self->priv->sink, NULL))
     goto error;
 
   filterpad = gst_element_get_static_pad (audioconvert0, "sink");
@@ -369,28 +371,16 @@ empathy_audio_sink_request_new_pad (GstElement *element,
     goto error;
 
   subpad = gst_ghost_pad_new ("sink", filterpad);
+  gst_object_unref (filterpad);
+
   if (!gst_element_add_pad (GST_ELEMENT (bin), subpad))
     goto error;
 
-
-  /* Ensure that state changes only happen _after_ the element has been added
-   * to the hash table. But add it to the bin first so we can create our
-   * ghostpad (if we create the ghostpad before adding it to the bin it will
-   * get unlinked) */
-  gst_element_set_locked_state (GST_ELEMENT (bin), TRUE);
   gst_bin_add (GST_BIN (self), bin);
 
   pad = gst_ghost_pad_new (name, subpad);
   g_assert (pad != NULL);
 
-  audiobin = audio_bin_new (pad, bin, volume, sink);
-
-  g_mutex_lock (self->priv->audio_bins_lock);
-  g_hash_table_insert (self->priv->audio_bins, pad, audiobin);
-  g_mutex_unlock (self->priv->audio_bins_lock);
-
-  gst_element_set_locked_state (GST_ELEMENT (bin), FALSE);
-
   if (!gst_element_sync_state_with_parent (bin))
     goto error;
 
@@ -400,16 +390,11 @@ empathy_audio_sink_request_new_pad (GstElement *element,
   if (!gst_element_add_pad (GST_ELEMENT (self), pad))
     goto error;
 
-
   return pad;
 
 error:
   if (pad != NULL)
     {
-      g_mutex_lock (self->priv->audio_bins_lock);
-      g_hash_table_remove (self->priv->audio_bins, pad);
-      g_mutex_unlock (self->priv->audio_bins_lock);
-
       gst_object_unref (pad);
     }
 
@@ -422,26 +407,17 @@ static void
 empathy_audio_sink_release_pad (GstElement *element,
   GstPad *pad)
 {
-  EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element);
-  AudioBin *abin;
-
-  g_mutex_lock (self->priv->audio_bins_lock);
-  abin = g_hash_table_lookup (self->priv->audio_bins, pad);
-  g_hash_table_steal (self->priv->audio_bins, pad);
-  g_mutex_unlock (self->priv->audio_bins_lock);
-
-  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);
+}
 
-  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);
-
-  audio_bin_free (abin);
+void
+empathy_audio_sink_set_echo_cancel (EmpathyGstAudioSink *sink,
+  gboolean echo_cancel)
+{
+  DEBUG ("Sink echo cancellation setting: %s", echo_cancel ? "on" : "off");
+  sink->priv->echo_cancel = echo_cancel;
+  if (sink->priv->sink != NULL)
+    empathy_audio_set_stream_properties (sink->priv->sink,
+      sink->priv->echo_cancel);
 }