2 * empathy-gst-audio-sink.c - Source for EmpathyGstAudioSink
3 * Copyright (C) 2008 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <gst/audio/audio.h>
26 #include <gst/farsight/fs-element-added-notifier.h>
28 #include "empathy-audio-sink.h"
31 G_DEFINE_TYPE(EmpathyGstAudioSink, empathy_audio_sink, GST_TYPE_BIN)
40 static guint signals[LAST_SIGNAL] = {0};
50 audio_bin_new (GstPad *pad,
55 AudioBin *result = g_slice_new0 (AudioBin);
59 result->volume = gst_object_ref (volume);
66 audio_bin_free (AudioBin *bin)
68 gst_object_unref (bin->volume);
69 g_slice_free (AudioBin, bin);
73 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE(
77 GST_STATIC_CAPS ( GST_AUDIO_INT_PAD_TEMPLATE_CAPS " ; "
78 GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
85 struct _EmpathyGstAudioSinkPrivate
87 gboolean dispose_has_run;
88 FsElementAddedNotifier *notifier;
92 /* Pad -> *owned* subbin hash */
93 GHashTable *audio_bins;
95 /* Mutex to hold while change the hash table */
96 GMutex *audio_bins_lock;
99 #define EMPATHY_GST_AUDIO_SINK_GET_PRIVATE(o) \
100 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SINK, \
101 EmpathyGstAudioSinkPrivate))
104 empathy_audio_sink_element_added_cb (FsElementAddedNotifier *notifier,
105 GstBin *bin, GstElement *element, EmpathyGstAudioSink *self)
107 EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
109 if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume"))
111 /* An element was added with a volume property, lets find its subbin and
112 * update the volume in it */
114 AudioBin *audio_bin = NULL;
117 g_mutex_lock (self->priv->audio_bins_lock);
118 g_hash_table_iter_init (&iter, priv->audio_bins);
120 while (g_hash_table_iter_next (&iter, NULL, &value))
124 if (gst_object_has_ancestor (GST_OBJECT (element),
125 GST_OBJECT (b->bin)))
132 if (audio_bin == NULL)
134 g_warning ("Element added that doesn't belong to us ?");
138 /* Set the old volume to 1 and the new volume to the volume */
139 g_object_set (audio_bin->volume, "volume", 1.0, NULL);
140 gst_object_unref (audio_bin->volume);
142 audio_bin->volume = gst_object_ref (element);
143 g_object_set (audio_bin->volume, "volume", self->priv->volume, NULL);
144 g_mutex_unlock (self->priv->audio_bins_lock);
149 empathy_audio_sink_init (EmpathyGstAudioSink *self)
151 EmpathyGstAudioSinkPrivate *priv;
153 priv = self->priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
157 priv->audio_bins = g_hash_table_new_full (g_direct_hash, g_direct_equal,
158 NULL, (GDestroyNotify) audio_bin_free);
160 priv->audio_bins_lock = g_mutex_new ();
162 priv->notifier = fs_element_added_notifier_new ();
163 g_signal_connect (priv->notifier, "element-added",
164 G_CALLBACK (empathy_audio_sink_element_added_cb), self);
167 static void empathy_audio_sink_dispose (GObject *object);
168 static void empathy_audio_sink_finalize (GObject *object);
170 static GstPad * empathy_audio_sink_request_new_pad (GstElement *self,
171 GstPadTemplate *templ,
174 static void empathy_audio_sink_release_pad (GstElement *self,
178 empathy_audio_sink_set_property (GObject *object,
179 guint property_id, const GValue *value, GParamSpec *pspec)
184 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (object),
185 g_value_get_double (value));
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
193 empathy_audio_sink_get_property (GObject *object,
194 guint property_id, GValue *value, GParamSpec *pspec)
199 g_value_set_double (value,
200 empathy_audio_sink_get_volume (EMPATHY_GST_AUDIO_SINK (object)));
203 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
208 empathy_audio_sink_class_init (EmpathyGstAudioSinkClass
209 *empathy_audio_sink_class)
211 GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_sink_class);
212 GstElementClass *element_class =
213 GST_ELEMENT_CLASS (empathy_audio_sink_class);
214 GParamSpec *param_spec;
216 gst_element_class_add_pad_template (element_class,
217 gst_static_pad_template_get (&sink_template));
219 g_type_class_add_private (empathy_audio_sink_class,
220 sizeof (EmpathyGstAudioSinkPrivate));
222 object_class->dispose = empathy_audio_sink_dispose;
223 object_class->finalize = empathy_audio_sink_finalize;
225 object_class->set_property = empathy_audio_sink_set_property;
226 object_class->get_property = empathy_audio_sink_get_property;
228 element_class->request_new_pad = empathy_audio_sink_request_new_pad;
229 element_class->release_pad = empathy_audio_sink_release_pad;
231 param_spec = g_param_spec_double ("volume", "Volume", "volume control",
233 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
234 g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
238 empathy_audio_sink_dispose (GObject *object)
240 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
241 EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
243 if (priv->dispose_has_run)
246 priv->dispose_has_run = TRUE;
248 if (priv->notifier != NULL)
249 g_object_unref (priv->notifier);
250 priv->notifier = NULL;
252 if (priv->audio_bins != NULL)
253 g_hash_table_unref (priv->audio_bins);
254 priv->audio_bins = NULL;
256 if (priv->audio_bins_lock != NULL)
257 g_mutex_free (priv->audio_bins_lock);
258 priv->audio_bins_lock = NULL;
260 if (G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose)
261 G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose (object);
265 empathy_audio_sink_finalize (GObject *object)
267 //EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
268 //EmpathyGstAudioSinkPrivate *priv =
269 // EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
271 /* free any data held directly by the object here */
273 G_OBJECT_CLASS (empathy_audio_sink_parent_class)->finalize (object);
277 empathy_audio_sink_new (void)
279 static gboolean registered = FALSE;
282 if (!gst_element_register (NULL, "empathyaudiosink",
283 GST_RANK_NONE, EMPATHY_TYPE_GST_AUDIO_SINK))
287 return gst_element_factory_make ("empathyaudiosink", NULL);
291 empathy_audio_sink_set_volume (EmpathyGstAudioSink *sink, gdouble volume)
293 EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink);
297 priv->volume = volume;
299 g_mutex_lock (priv->audio_bins_lock);
301 g_hash_table_iter_init (&iter, priv->audio_bins);
302 while (g_hash_table_iter_next (&iter, NULL, &value))
305 g_object_set (b->volume, "volume", volume, NULL);
308 g_mutex_unlock (priv->audio_bins_lock);
312 empathy_audio_sink_get_volume (EmpathyGstAudioSink *sink)
314 EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink);
319 empathy_audio_sink_request_new_pad (GstElement *element,
320 GstPadTemplate *templ,
323 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element);
324 GstElement *bin, *sink, *volume, *resample, *audioconvert0, *audioconvert1;
326 GstPad *subpad, *filterpad;
329 bin = gst_bin_new (NULL);
331 audioconvert0 = gst_element_factory_make ("audioconvert", NULL);
332 if (audioconvert0 == NULL)
335 gst_bin_add (GST_BIN (bin), audioconvert0);
337 resample = gst_element_factory_make ("audioresample", NULL);
338 if (resample == NULL)
341 gst_bin_add (GST_BIN (bin), resample);
343 audioconvert1 = gst_element_factory_make ("audioconvert", NULL);
344 if (audioconvert1 == NULL)
347 gst_bin_add (GST_BIN (bin), audioconvert1);
349 volume = gst_element_factory_make ("volume", NULL);
353 gst_bin_add (GST_BIN (bin), volume);
355 sink = gst_element_factory_make ("gconfaudiosink", NULL);
359 gst_bin_add (GST_BIN (bin), sink);
360 fs_element_added_notifier_add (self->priv->notifier, GST_BIN (sink));
362 if (!gst_element_link_many (audioconvert0, resample, audioconvert1,
366 filterpad = gst_element_get_static_pad (audioconvert0, "sink");
368 if (filterpad == NULL)
371 subpad = gst_ghost_pad_new ("sink", filterpad);
372 if (!gst_element_add_pad (GST_ELEMENT (bin), subpad))
376 /* Ensure that state changes only happen _after_ the element has been added
377 * to the hash table. But add it to the bin first so we can create our
378 * ghostpad (if we create the ghostpad before adding it to the bin it will
380 gst_element_set_locked_state (GST_ELEMENT (bin), TRUE);
381 gst_bin_add (GST_BIN (self), bin);
383 pad = gst_ghost_pad_new (name, subpad);
384 g_assert (pad != NULL);
386 audiobin = audio_bin_new (pad, bin, volume, sink);
388 g_mutex_lock (self->priv->audio_bins_lock);
389 g_hash_table_insert (self->priv->audio_bins, pad, audiobin);
390 g_mutex_unlock (self->priv->audio_bins_lock);
392 gst_element_set_locked_state (GST_ELEMENT (bin), FALSE);
394 if (!gst_element_sync_state_with_parent (bin))
397 if (!gst_pad_set_active (pad, TRUE))
400 if (!gst_element_add_pad (GST_ELEMENT (self), pad))
409 g_mutex_lock (self->priv->audio_bins_lock);
410 g_hash_table_remove (self->priv->audio_bins, pad);
411 g_mutex_unlock (self->priv->audio_bins_lock);
413 gst_object_unref (pad);
416 gst_object_unref (bin);
417 g_warning ("Failed to create output subpipeline");
422 empathy_audio_sink_release_pad (GstElement *element,
425 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element);
428 g_mutex_lock (self->priv->audio_bins_lock);
429 abin = g_hash_table_lookup (self->priv->audio_bins, pad);
430 g_hash_table_steal (self->priv->audio_bins, pad);
431 g_mutex_unlock (self->priv->audio_bins_lock);
435 g_warning ("Releasing a pad that doesn't belong to us ?");
439 gst_pad_set_active (pad, FALSE);
440 gst_element_remove_pad (element, pad);
442 gst_element_set_locked_state (abin->bin, TRUE);
443 gst_element_set_state (abin->bin, GST_STATE_NULL);
444 gst_bin_remove (GST_BIN (self), abin->bin);
446 audio_bin_free (abin);