diff options
Diffstat (limited to 'src/empathy-audio-sink.c')
-rw-r--r-- | src/empathy-audio-sink.c | 274 |
1 files changed, 228 insertions, 46 deletions
diff --git a/src/empathy-audio-sink.c b/src/empathy-audio-sink.c index 1d2169593..31fffff10 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); } |