Fix broken markup in Finnish user docs translation
[nijm-empathy.git] / src / empathy-audio-sink.c
blobf3a78bf3fc1ac16906bc80063574fc5c7fc0aaf6
1 /*
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
21 #include "config.h"
22 #include "empathy-audio-sink.h"
24 #include <gst/audio/streamvolume.h>
26 #include "empathy-audio-utils.h"
28 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
29 #include "empathy-debug.h"
31 G_DEFINE_TYPE(EmpathyGstAudioSink, empathy_audio_sink, GST_TYPE_BIN)
33 /* signal enum */
34 #if 0
35 enum
37 LAST_SIGNAL
40 static guint signals[LAST_SIGNAL] = {0};
41 #endif
43 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE(
44 "sink%d",
45 GST_PAD_SINK,
46 GST_PAD_REQUEST,
47 GST_STATIC_CAPS ( "audio/x-raw" )
50 enum {
51 PROP_VOLUME = 1,
54 struct _EmpathyGstAudioSinkPrivate
56 GstElement *sink;
57 gboolean echo_cancel;
58 gdouble volume;
59 gint volume_idle_id;
60 GMutex volume_mutex;
63 #define EMPATHY_GST_AUDIO_SINK_GET_PRIVATE(o) \
64 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SINK, \
65 EmpathyGstAudioSinkPrivate))
67 static void
68 empathy_audio_sink_init (EmpathyGstAudioSink *self)
70 self->priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
71 self->priv->echo_cancel = TRUE;
72 g_mutex_init (&self->priv->volume_mutex);
75 static GstPad * empathy_audio_sink_request_new_pad (GstElement *self,
76 GstPadTemplate *templ,
77 const gchar* name,
78 const GstCaps *caps);
80 static void empathy_audio_sink_release_pad (GstElement *self,
81 GstPad *pad);
83 static void
84 empathy_audio_sink_set_property (GObject *object,
85 guint property_id, const GValue *value, GParamSpec *pspec)
87 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
88 switch (property_id)
90 case PROP_VOLUME:
91 g_mutex_lock (&self->priv->volume_mutex);
92 self->priv->volume = g_value_get_double (value);
93 g_mutex_unlock (&self->priv->volume_mutex);
94 break;
95 default:
96 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
100 static void
101 empathy_audio_sink_get_property (GObject *object,
102 guint property_id, GValue *value, GParamSpec *pspec)
104 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
105 switch (property_id)
107 case PROP_VOLUME:
108 g_value_set_double (value, self->priv->volume);
109 break;
110 default:
111 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
115 static void
116 empathy_audio_sink_dispose (GObject *object)
118 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
119 EmpathyGstAudioSinkPrivate *priv = self->priv;
121 if (priv->volume_idle_id != 0)
122 g_source_remove (priv->volume_idle_id);
123 priv->volume_idle_id = 0;
125 g_mutex_clear (&self->priv->volume_mutex);
127 /* release any references held by the object here */
128 if (G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose)
129 G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose (object);
132 static void
133 empathy_audio_sink_class_init (EmpathyGstAudioSinkClass
134 *empathy_audio_sink_class)
136 GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_sink_class);
137 GstElementClass *element_class =
138 GST_ELEMENT_CLASS (empathy_audio_sink_class);
139 GParamSpec *param_spec;
141 gst_element_class_add_pad_template (element_class,
142 gst_static_pad_template_get (&sink_template));
144 g_type_class_add_private (empathy_audio_sink_class,
145 sizeof (EmpathyGstAudioSinkPrivate));
147 object_class->set_property = empathy_audio_sink_set_property;
148 object_class->get_property = empathy_audio_sink_get_property;
149 object_class->dispose = empathy_audio_sink_dispose;
151 element_class->request_new_pad = empathy_audio_sink_request_new_pad;
152 element_class->release_pad = empathy_audio_sink_release_pad;
154 param_spec = g_param_spec_double ("volume", "Volume", "volume control",
155 0.0, 5.0, 1.0,
156 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
157 g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
160 GstElement *
161 empathy_audio_sink_new (void)
163 static gboolean registered = FALSE;
165 if (!registered) {
166 if (!gst_element_register (NULL, "empathyaudiosink",
167 GST_RANK_NONE, EMPATHY_TYPE_GST_AUDIO_SINK))
168 return NULL;
169 registered = TRUE;
171 return gst_element_factory_make ("empathyaudiosink", NULL);
174 void
175 empathy_audio_sink_set_volume (EmpathyGstAudioSink *sink, gdouble volume)
177 g_object_set (sink, "volume", volume, NULL);
180 gdouble
181 empathy_audio_sink_get_volume (EmpathyGstAudioSink *sink)
183 EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink);
185 return priv->volume;
188 static GstElement *
189 create_sink (EmpathyGstAudioSink *self)
191 GstElement *sink;
192 const gchar *description;
194 description = g_getenv ("EMPATHY_AUDIO_SINK");
196 if (description != NULL)
198 GError *error = NULL;
200 sink = gst_parse_bin_from_description (description, TRUE, &error);
201 if (sink == NULL)
203 DEBUG ("Failed to create bin %s: %s", description, error->message);
204 g_error_free (error);
207 return sink;
210 /* Use pulsesink as default */
211 sink = gst_element_factory_make ("pulsesink", NULL);
212 if (sink == NULL)
213 return NULL;
215 empathy_audio_set_stream_properties (sink, self->priv->echo_cancel);
217 /* Set latency (buffering on the PulseAudio side) of 40ms and transfer data
218 * in 10ms chunks */
219 g_object_set (sink,
220 "buffer-time", (gint64) 40000,
221 "latency-time", (gint64) 10000,
222 NULL);
224 return sink;
227 static gboolean
228 empathy_audio_sink_volume_idle_updated (gpointer user_data)
230 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (user_data);
232 g_mutex_lock (&self->priv->volume_mutex);
233 self->priv->volume_idle_id = 0;
234 g_mutex_unlock (&self->priv->volume_mutex);
236 g_object_notify (G_OBJECT (self), "volume");
238 return FALSE;
241 static void
242 empathy_audio_sink_volume_updated (GObject *object,
243 GParamSpec *pspec,
244 gpointer user_data)
246 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (user_data);
247 gdouble volume;
249 g_mutex_lock (&self->priv->volume_mutex);
251 g_object_get (object, "volume", &volume, NULL);
252 if (self->priv->volume == volume)
253 goto out;
255 self->priv->volume = volume;
256 if (self->priv->volume_idle_id == 0)
257 self->priv->volume_idle_id = g_idle_add (
258 empathy_audio_sink_volume_idle_updated, self);
260 out:
261 g_mutex_unlock (&self->priv->volume_mutex);
264 static gboolean
265 empathy_audio_sink_volume_idle_setup (gpointer user_data)
267 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (user_data);
268 gdouble volume;
270 g_mutex_lock (&self->priv->volume_mutex);
271 self->priv->volume_idle_id = 0;
272 g_mutex_unlock (&self->priv->volume_mutex);
274 /* We can't do a bidirection bind as the ::notify comes from another
275 * thread, for other bits of empathy it's most simpler if it comes from
276 * the main thread */
277 g_object_bind_property (self, "volume", self->priv->sink, "volume",
278 G_BINDING_DEFAULT);
280 /* sync and callback for bouncing */
281 g_object_get (self->priv->sink, "volume", &volume, NULL);
282 g_object_set (self, "volume", volume, NULL);
283 g_signal_connect (self->priv->sink, "notify::volume",
284 G_CALLBACK (empathy_audio_sink_volume_updated), self);
286 return FALSE;
289 static GstPad *
290 empathy_audio_sink_request_new_pad (GstElement *element,
291 GstPadTemplate *templ,
292 const gchar* name,
293 const GstCaps *caps)
295 EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element);
296 GstElement *bin, *resample, *audioconvert0, *audioconvert1;
297 GstPad *pad = NULL;
298 GstPad *subpad, *filterpad;
300 bin = gst_bin_new (NULL);
302 audioconvert0 = gst_element_factory_make ("audioconvert", NULL);
303 if (audioconvert0 == NULL)
304 goto error;
306 gst_bin_add (GST_BIN (bin), audioconvert0);
308 resample = gst_element_factory_make ("audioresample", NULL);
309 if (resample == NULL)
310 goto error;
312 gst_bin_add (GST_BIN (bin), resample);
314 audioconvert1 = gst_element_factory_make ("audioconvert", NULL);
315 if (audioconvert1 == NULL)
316 goto error;
318 gst_bin_add (GST_BIN (bin), audioconvert1);
320 self->priv->sink = create_sink (self);
321 if (self->priv->sink == NULL)
322 goto error;
324 if (GST_IS_STREAM_VOLUME (self->priv->sink))
326 g_mutex_lock (&self->priv->volume_mutex);
327 if (self->priv->volume_idle_id == 0)
328 self->priv->volume_idle_id = g_idle_add (
329 empathy_audio_sink_volume_idle_setup, self);
330 g_mutex_unlock (&self->priv->volume_mutex);
332 else
334 gchar *n = gst_element_get_name (self->priv->sink);
336 DEBUG ("Element %s doesn't support volume", n);
337 g_free (n);
340 gst_bin_add (GST_BIN (bin), self->priv->sink);
342 if (!gst_element_link_many (audioconvert0, resample, audioconvert1,
343 self->priv->sink, NULL))
344 goto error;
346 filterpad = gst_element_get_static_pad (audioconvert0, "sink");
348 if (filterpad == NULL)
349 goto error;
351 subpad = gst_ghost_pad_new ("sink", filterpad);
352 gst_object_unref (filterpad);
354 if (!gst_element_add_pad (GST_ELEMENT (bin), subpad))
355 goto error;
357 gst_bin_add (GST_BIN (self), bin);
359 pad = gst_ghost_pad_new (name, subpad);
360 g_assert (pad != NULL);
362 if (!gst_element_sync_state_with_parent (bin))
363 goto error;
365 if (!gst_pad_set_active (pad, TRUE))
366 goto error;
368 if (!gst_element_add_pad (GST_ELEMENT (self), pad))
369 goto error;
371 return pad;
373 error:
374 if (pad != NULL)
376 gst_object_unref (pad);
379 gst_object_unref (bin);
380 g_warning ("Failed to create output subpipeline");
381 return NULL;
384 static void
385 empathy_audio_sink_release_pad (GstElement *element,
386 GstPad *pad)
388 gst_pad_set_active (pad, FALSE);
389 gst_element_remove_pad (element, pad);
392 void
393 empathy_audio_sink_set_echo_cancel (EmpathyGstAudioSink *sink,
394 gboolean echo_cancel)
396 DEBUG ("Sink echo cancellation setting: %s", echo_cancel ? "on" : "off");
397 sink->priv->echo_cancel = echo_cancel;
398 if (sink->priv->sink != NULL)
399 empathy_audio_set_stream_properties (sink->priv->sink,
400 sink->priv->echo_cancel);