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
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
)
40 static guint signals
[LAST_SIGNAL
] = {0};
43 static GstStaticPadTemplate sink_template
= GST_STATIC_PAD_TEMPLATE(
47 GST_STATIC_CAPS ( "audio/x-raw" )
54 struct _EmpathyGstAudioSinkPrivate
63 #define EMPATHY_GST_AUDIO_SINK_GET_PRIVATE(o) \
64 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SINK, \
65 EmpathyGstAudioSinkPrivate))
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
,
80 static void empathy_audio_sink_release_pad (GstElement
*self
,
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
);
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
);
96 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
101 empathy_audio_sink_get_property (GObject
*object
,
102 guint property_id
, GValue
*value
, GParamSpec
*pspec
)
104 EmpathyGstAudioSink
*self
= EMPATHY_GST_AUDIO_SINK (object
);
108 g_value_set_double (value
, self
->priv
->volume
);
111 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
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
);
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",
156 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
157 g_object_class_install_property (object_class
, PROP_VOLUME
, param_spec
);
161 empathy_audio_sink_new (void)
163 static gboolean registered
= FALSE
;
166 if (!gst_element_register (NULL
, "empathyaudiosink",
167 GST_RANK_NONE
, EMPATHY_TYPE_GST_AUDIO_SINK
))
171 return gst_element_factory_make ("empathyaudiosink", NULL
);
175 empathy_audio_sink_set_volume (EmpathyGstAudioSink
*sink
, gdouble volume
)
177 g_object_set (sink
, "volume", volume
, NULL
);
181 empathy_audio_sink_get_volume (EmpathyGstAudioSink
*sink
)
183 EmpathyGstAudioSinkPrivate
*priv
= EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink
);
189 create_sink (EmpathyGstAudioSink
*self
)
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
);
203 DEBUG ("Failed to create bin %s: %s", description
, error
->message
);
204 g_error_free (error
);
210 /* Use pulsesink as default */
211 sink
= gst_element_factory_make ("pulsesink", 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
220 "buffer-time", (gint64
) 40000,
221 "latency-time", (gint64
) 10000,
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");
242 empathy_audio_sink_volume_updated (GObject
*object
,
246 EmpathyGstAudioSink
*self
= EMPATHY_GST_AUDIO_SINK (user_data
);
249 g_mutex_lock (&self
->priv
->volume_mutex
);
251 g_object_get (object
, "volume", &volume
, NULL
);
252 if (self
->priv
->volume
== volume
)
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
);
261 g_mutex_unlock (&self
->priv
->volume_mutex
);
265 empathy_audio_sink_volume_idle_setup (gpointer user_data
)
267 EmpathyGstAudioSink
*self
= EMPATHY_GST_AUDIO_SINK (user_data
);
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
277 g_object_bind_property (self
, "volume", self
->priv
->sink
, "volume",
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
);
290 empathy_audio_sink_request_new_pad (GstElement
*element
,
291 GstPadTemplate
*templ
,
295 EmpathyGstAudioSink
*self
= EMPATHY_GST_AUDIO_SINK (element
);
296 GstElement
*bin
, *resample
, *audioconvert0
, *audioconvert1
;
298 GstPad
*subpad
, *filterpad
;
300 bin
= gst_bin_new (NULL
);
302 audioconvert0
= gst_element_factory_make ("audioconvert", NULL
);
303 if (audioconvert0
== NULL
)
306 gst_bin_add (GST_BIN (bin
), audioconvert0
);
308 resample
= gst_element_factory_make ("audioresample", NULL
);
309 if (resample
== NULL
)
312 gst_bin_add (GST_BIN (bin
), resample
);
314 audioconvert1
= gst_element_factory_make ("audioconvert", NULL
);
315 if (audioconvert1
== NULL
)
318 gst_bin_add (GST_BIN (bin
), audioconvert1
);
320 self
->priv
->sink
= create_sink (self
);
321 if (self
->priv
->sink
== NULL
)
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
);
334 gchar
*n
= gst_element_get_name (self
->priv
->sink
);
336 DEBUG ("Element %s doesn't support volume", 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
))
346 filterpad
= gst_element_get_static_pad (audioconvert0
, "sink");
348 if (filterpad
== NULL
)
351 subpad
= gst_ghost_pad_new ("sink", filterpad
);
352 gst_object_unref (filterpad
);
354 if (!gst_element_add_pad (GST_ELEMENT (bin
), subpad
))
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
))
365 if (!gst_pad_set_active (pad
, TRUE
))
368 if (!gst_element_add_pad (GST_ELEMENT (self
), pad
))
376 gst_object_unref (pad
);
379 gst_object_unref (bin
);
380 g_warning ("Failed to create output subpipeline");
385 empathy_audio_sink_release_pad (GstElement
*element
,
388 gst_pad_set_active (pad
, FALSE
);
389 gst_element_remove_pad (element
, pad
);
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
);