]> git.0d.be Git - empathy.git/commitdiff
Implement request pads for multiple sinks
authorSjoerd Simons <sjoerd.simons@collabora.co.uk>
Fri, 11 Mar 2011 19:01:17 +0000 (19:01 +0000)
committerEmilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
Thu, 9 Jun 2011 09:20:06 +0000 (10:20 +0100)
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
src/empathy-audio-sink.h

index 1d2169593f73739dad371b861164a48239a6f575..31fffff108b8d86f362cf707206b04a0f6cb6436 100644 (file)
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <gst/audio/audio.h>
 #include <gst/farsight/fs-element-added-notifier.h>
 
 #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);
 }
index 21ebf2b5addd76bd710c1f264073cf735a4030b0..cc21fc4675d7ae691b79f4ef343577127f3c651b 100644 (file)
@@ -28,6 +28,7 @@ G_BEGIN_DECLS
 
 typedef struct _EmpathyGstAudioSink EmpathyGstAudioSink;
 typedef struct _EmpathyGstAudioSinkClass EmpathyGstAudioSinkClass;
+typedef struct _EmpathyGstAudioSinkPrivate EmpathyGstAudioSinkPrivate;
 
 struct _EmpathyGstAudioSinkClass {
     GstBinClass parent_class;
@@ -35,6 +36,7 @@ struct _EmpathyGstAudioSinkClass {
 
 struct _EmpathyGstAudioSink {
     GstBin parent;
+    EmpathyGstAudioSinkPrivate *priv;
 };
 
 GType empathy_audio_sink_get_type (void);