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;
96 #define EMPATHY_GST_AUDIO_SINK_GET_PRIVATE(o) \
97 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SINK, \
98 EmpathyGstAudioSinkPrivate))
101 empathy_audio_sink_element_added_cb (FsElementAddedNotifier *notifier,
102 GstBin *bin, GstElement *element, EmpathyGstAudioSink *self)
104 EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
106 if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume"))
108 /* An element was added with a volume property, lets find its subbin and
109 * update the volume in it */
111 AudioBin *audio_bin = NULL;
114 g_hash_table_iter_init (&iter, priv->audio_bins);
116 while (g_hash_table_iter_next (&iter, NULL, &value))
120 if (gst_object_has_ancestor (GST_OBJECT (element),
121 GST_OBJECT (b->bin)))
128 if (audio_bin == NULL)
130 g_warning ("Element added that doesn't belong to us ?");
134 /* Set the old volume to 1 and the new volume to the volume */
135 g_object_set (audio_bin->volume, "volume", 1.0, NULL);
136 gst_object_unref (audio_bin->volume);
138 audio_bin->volume = gst_object_ref (element);
139 g_object_set (audio_bin->volume, "volume", self->priv->volume, NULL);
144 empathy_audio_sink_init (EmpathyGstAudioSink *self)
146 EmpathyGstAudioSinkPrivate *priv;
148 priv = self->priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
152 priv->audio_bins = g_hash_table_new_full (g_direct_hash, g_direct_equal,
153 NULL, (GDestroyNotify) audio_bin_free);
155 priv->notifier = fs_element_added_notifier_new ();
156 g_signal_connect (priv->notifier, "element-added",
157 G_CALLBACK (empathy_audio_sink_element_added_cb), self);
160 static void empathy_audio_sink_dispose (GObject *object);
161 static void empathy_audio_sink_finalize (GObject *object);
163 static GstPad * empathy_audio_sink_request_new_pad (GstElement *self,
164 GstPadTemplate *templ,
167 static void empathy_audio_sink_release_pad (GstElement *self,
171 empathy_audio_sink_set_property (GObject *object,
172 guint property_id, const GValue *value, GParamSpec *pspec)
177 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (object),
178 g_value_get_double (value));
181 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
186 empathy_audio_sink_get_property (GObject *object,
187 guint property_id, GValue *value, GParamSpec *pspec)
192 g_value_set_double (value,
193 empathy_audio_sink_get_volume (EMPATHY_GST_AUDIO_SINK (object)));
196 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
201 empathy_audio_sink_class_init (EmpathyGstAudioSinkClass
202 *empathy_audio_sink_class)
204 GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_sink_class);
205 GstElementClass *element_class =
206 GST_ELEMENT_CLASS (empathy_audio_sink_class);
207 GParamSpec *param_spec;
209 gst_element_class_add_pad_template (element_class,
210 gst_static_pad_template_get (&sink_template));
212 g_type_class_add_private (empathy_audio_sink_class,
213 sizeof (EmpathyGstAudioSinkPrivate));
215 object_class->dispose = empathy_audio_sink_dispose;
216 object_class->finalize = empathy_audio_sink_finalize;
218 object_class->set_property = empathy_audio_sink_set_property;
219 object_class->get_property = empathy_audio_sink_get_property;
221 element_class->request_new_pad = empathy_audio_sink_request_new_pad;
222 element_class->release_pad = empathy_audio_sink_release_pad;
224 param_spec = g_param_spec_double ("volume", "Volume", "volume control",
226 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
227 g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
231 empathy_audio_sink_dispose (GObject *object)
233 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
234 EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
236 if (priv->dispose_has_run)
239 priv->dispose_has_run = TRUE;
241 if (priv->notifier != NULL)
242 g_object_unref (priv->notifier);
243 priv->notifier = NULL;
245 if (priv->audio_bins != NULL)
246 g_hash_table_unref (priv->audio_bins);
247 priv->audio_bins = NULL;
249 if (G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose)
250 G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose (object);
254 empathy_audio_sink_finalize (GObject *object)
256 //EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
257 //EmpathyGstAudioSinkPrivate *priv =
258 // EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
260 /* free any data held directly by the object here */
262 G_OBJECT_CLASS (empathy_audio_sink_parent_class)->finalize (object);
266 empathy_audio_sink_new (void)
268 static gboolean registered = FALSE;
271 if (!gst_element_register (NULL, "empathyaudiosink",
272 GST_RANK_NONE, EMPATHY_TYPE_GST_AUDIO_SINK))
276 return gst_element_factory_make ("empathyaudiosink", NULL);
280 empathy_audio_sink_set_volume (EmpathyGstAudioSink *sink, gdouble volume)
282 EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink);
286 priv->volume = volume;
287 g_hash_table_iter_init (&iter, priv->audio_bins);
289 while (g_hash_table_iter_next (&iter, NULL, &value))
292 g_object_set (b->volume, "volume", volume, NULL);
297 empathy_audio_sink_get_volume (EmpathyGstAudioSink *sink)
299 EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink);
304 empathy_audio_sink_request_new_pad (GstElement *element,
305 GstPadTemplate *templ,
308 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element);
309 GstElement *bin, *sink, *volume, *resample, *audioconvert0, *audioconvert1;
311 GstPad *subpad, *filterpad;
314 bin = gst_bin_new (NULL);
316 audioconvert0 = gst_element_factory_make ("audioconvert", NULL);
317 if (audioconvert0 == NULL)
320 gst_bin_add (GST_BIN (bin), audioconvert0);
322 resample = gst_element_factory_make ("audioresample", NULL);
323 if (resample == NULL)
326 gst_bin_add (GST_BIN (bin), resample);
328 audioconvert1 = gst_element_factory_make ("audioconvert", NULL);
329 if (audioconvert1 == NULL)
332 gst_bin_add (GST_BIN (bin), audioconvert1);
334 volume = gst_element_factory_make ("volume", NULL);
338 gst_bin_add (GST_BIN (bin), volume);
340 sink = gst_element_factory_make ("gconfaudiosink", NULL);
344 gst_bin_add (GST_BIN (bin), sink);
345 fs_element_added_notifier_add (self->priv->notifier, GST_BIN (sink));
347 if (!gst_element_link_many (audioconvert0, resample, audioconvert1,
351 filterpad = gst_element_get_static_pad (audioconvert0, "sink");
353 if (filterpad == NULL)
356 subpad = gst_ghost_pad_new ("sink", filterpad);
357 if (!gst_element_add_pad (GST_ELEMENT (bin), subpad))
361 /* Ensure that state changes only happen _after_ the element has been added
362 * to the hash table. But add it to the bin first so we can create our
363 * ghostpad (if we create the ghostpad before adding it to the bin it will
365 gst_element_set_locked_state (GST_ELEMENT (bin), TRUE);
366 gst_bin_add (GST_BIN (self), bin);
368 pad = gst_ghost_pad_new (name, subpad);
369 g_assert (pad != NULL);
371 audiobin = audio_bin_new (pad, bin, volume, sink);
373 g_hash_table_insert (self->priv->audio_bins, pad, audiobin);
375 gst_element_set_locked_state (GST_ELEMENT (bin), FALSE);
377 if (!gst_element_sync_state_with_parent (bin))
380 if (!gst_pad_set_active (pad, TRUE))
383 if (!gst_element_add_pad (GST_ELEMENT (self), pad))
392 g_hash_table_remove (self->priv->audio_bins, pad);
393 gst_object_unref (pad);
396 gst_object_unref (bin);
397 g_warning ("Failed to create output subpipeline");
402 empathy_audio_sink_release_pad (GstElement *element,
405 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element);
408 abin = g_hash_table_lookup (self->priv->audio_bins, pad);
409 g_hash_table_steal (self->priv->audio_bins, pad);
413 g_warning ("Releasing a pad that doesn't belong to us ?");
417 gst_pad_set_active (pad, FALSE);
418 gst_element_remove_pad (element, pad);
420 gst_element_set_locked_state (abin->bin, TRUE);
421 gst_element_set_state (abin->bin, GST_STATE_NULL);
422 gst_bin_remove (GST_BIN (self), abin->bin);
424 audio_bin_free (abin);