[empathy: 52/99] Implement request pads for multiple sinks



commit e9fb5d29ff44ae35411b02c3715ae9650b61dd25
Author: Sjoerd Simons <sjoerd simons collabora co uk>
Date:   Fri Mar 11 19:01:17 2011 +0000

    Implement request pads for multiple sinks
    
    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 |  274 ++++++++++++++++++++++++++++++++++++++--------
 src/empathy-audio-sink.h |    2 +
 2 files changed, 230 insertions(+), 46 deletions(-)
---
diff --git a/src/empathy-audio-sink.c b/src/empathy-audio-sink.c
index 1d21695..31fffff 100644
--- a/src/empathy-audio-sink.c
+++ b/src/empathy-audio-sink.c
@@ -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);
 }
diff --git a/src/empathy-audio-sink.h b/src/empathy-audio-sink.h
index 21ebf2b..cc21fc4 100644
--- a/src/empathy-audio-sink.h
+++ b/src/empathy-audio-sink.h
@@ -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);



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