]> git.0d.be Git - empathy.git/blob - src/empathy-audio-src.c
Merge branch 'gnome-3-4'
[empathy.git] / src / empathy-audio-src.c
1 /*
2  * empathy-gst-audio-src.c - Source for EmpathyGstAudioSrc
3  * Copyright (C) 2008 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include <gst/interfaces/mixer.h>
27
28 #include <libempathy/empathy-utils.h>
29 #include <libempathy-gtk/empathy-call-utils.h>
30
31 #include "empathy-audio-src.h"
32
33 #include "empathy-mic-monitor.h"
34
35 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
36 #include <libempathy/empathy-debug.h>
37
38 G_DEFINE_TYPE(EmpathyGstAudioSrc, empathy_audio_src, GST_TYPE_BIN)
39
40 /* signal enum */
41 enum
42 {
43     PEAK_LEVEL_CHANGED,
44     RMS_LEVEL_CHANGED,
45     LAST_SIGNAL
46 };
47
48 static guint signals[LAST_SIGNAL] = {0};
49
50 enum {
51     PROP_VOLUME = 1,
52     PROP_MUTE,
53     PROP_RMS_LEVEL,
54     PROP_PEAK_LEVEL,
55     PROP_MICROPHONE,
56 };
57
58 /* private structure */
59 struct _EmpathyGstAudioSrcPrivate
60 {
61   gboolean dispose_has_run;
62   GstElement *src;
63   GstElement *level;
64
65   EmpathyMicMonitor *mic_monitor;
66
67   /* 0 if not known yet */
68   guint source_output_idx;
69   /* G_MAXUINT if not known yet */
70   guint source_idx;
71
72   gdouble peak_level;
73   gdouble rms_level;
74
75   gdouble volume;
76   gboolean mute;
77   /* the mixer track on src we follow and adjust */
78   GstMixerTrack *track;
79
80   GMutex lock;
81   guint level_idle_id;
82   guint volume_idle_id;
83 };
84
85 #define EMPATHY_GST_AUDIO_SRC_GET_PRIVATE(o) \
86   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SRC, \
87   EmpathyGstAudioSrcPrivate))
88
89 /* There is no predefined maximum channels by gstreamer, just pick 32, which is
90  * the same as the pulseaudio maximum */
91 #define MAX_MIC_CHANNELS 32
92
93 static void
94 empathy_audio_set_hw_mute (EmpathyGstAudioSrc *self, gboolean mute)
95 {
96   g_mutex_lock (&self->priv->lock);
97   /* If there is no mixer available ignore the setting */
98   if (self->priv->track == NULL)
99     goto out;
100
101   gst_mixer_set_mute (GST_MIXER (self->priv->src), self->priv->track, mute);
102
103 out:
104   g_mutex_unlock (&self->priv->lock);
105   self->priv->mute = mute;
106 }
107
108 static gboolean
109 empathy_audio_src_get_hw_mute (EmpathyGstAudioSrc *self)
110 {
111   gboolean result = self->priv->mute;
112
113   g_mutex_lock (&self->priv->lock);
114   if (self->priv->track == NULL)
115     goto out;
116
117   result = GST_MIXER_TRACK_HAS_FLAG (self->priv->track, GST_MIXER_TRACK_MUTE);
118 out:
119   g_mutex_unlock (&self->priv->lock);
120
121   return result;
122 }
123
124 static void
125 empathy_audio_src_set_hw_volume (EmpathyGstAudioSrc *self,
126     gdouble volume)
127 {
128   gint volumes[MAX_MIC_CHANNELS];
129   int i;
130
131   g_mutex_lock (&self->priv->lock);
132   /* If there is no mixer available ignore the setting */
133   if (self->priv->track == NULL)
134     goto out;
135
136   for (i = 0; i < MAX_MIC_CHANNELS; i++)
137     volumes[i] = self->priv->track->max_volume * volume;
138
139   gst_mixer_set_volume (GST_MIXER (self->priv->src),
140     self->priv->track, volumes);
141
142 out:
143    g_mutex_unlock (&self->priv->lock);
144
145   self->priv->volume = volume;
146 }
147
148 static gdouble
149 empathy_audio_src_get_hw_volume (EmpathyGstAudioSrc *self)
150 {
151   gint volumes[MAX_MIC_CHANNELS];
152   gdouble result = self->priv->volume;
153
154   g_mutex_lock (&self->priv->lock);
155   if (self->priv->track == NULL)
156     goto out;
157
158   gst_mixer_get_volume (GST_MIXER (self->priv->src),
159     self->priv->track, volumes);
160   result = volumes[0]/(gdouble)self->priv->track->max_volume;
161
162 out:
163   g_mutex_unlock (&self->priv->lock);
164
165   return result;
166 }
167
168
169 gboolean
170 empathy_audio_src_supports_changing_mic (EmpathyGstAudioSrc *self)
171 {
172   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
173   GObjectClass *object_class;
174
175   object_class = G_OBJECT_GET_CLASS (priv->src);
176
177   return (g_object_class_find_property (object_class,
178           "source-output-index") != NULL);
179 }
180
181 static guint
182 empathy_audio_src_get_mic_index (EmpathyGstAudioSrc *self)
183 {
184   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
185   guint audio_src_idx = PA_INVALID_INDEX;
186
187   if (empathy_audio_src_supports_changing_mic (self))
188     g_object_get (priv->src,
189       "source-output-index", &audio_src_idx,
190       NULL);
191
192   return audio_src_idx;
193 }
194
195 static void
196 empathy_audio_src_microphone_changed_cb (EmpathyMicMonitor *monitor,
197     guint source_output_idx,
198     guint source_idx,
199     gpointer user_data)
200 {
201   EmpathyGstAudioSrc *self = user_data;
202   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
203   guint audio_src_idx;
204
205   audio_src_idx = empathy_audio_src_get_mic_index (self);
206
207   if (source_output_idx == PA_INVALID_INDEX
208       || source_output_idx != audio_src_idx)
209     return;
210
211   if (priv->source_idx == source_idx)
212     return;
213
214   priv->source_idx = source_idx;
215   g_object_notify (G_OBJECT (self), "microphone");
216 }
217
218 static void
219 empathy_audio_src_get_current_mic_cb (GObject *source_object,
220     GAsyncResult *result,
221     gpointer user_data)
222 {
223   EmpathyMicMonitor *monitor = EMPATHY_MIC_MONITOR (source_object);
224   EmpathyGstAudioSrc *self = user_data;
225   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
226   guint source_idx;
227   GError *error = NULL;
228
229   source_idx = empathy_mic_monitor_get_current_mic_finish (monitor, result, &error);
230
231   if (error != NULL)
232     {
233       DEBUG ("Failed to get current mic: %s", error->message);
234       g_clear_error (&error);
235       return;
236     }
237
238   if (priv->source_idx == source_idx)
239     return;
240
241   priv->source_idx = source_idx;
242   g_object_notify (G_OBJECT (self), "microphone");
243 }
244
245 static void
246 empathy_audio_src_source_output_index_notify (GObject *object,
247     GParamSpec *pspec,
248     EmpathyGstAudioSrc *self)
249 {
250   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
251   guint source_output_idx;
252
253   source_output_idx = empathy_audio_src_get_mic_index (self);
254
255   if (source_output_idx == PA_INVALID_INDEX)
256     return;
257
258   if (priv->source_output_idx == source_output_idx)
259     return;
260
261   /* It's actually changed. */
262   priv->source_output_idx = source_output_idx;
263
264   empathy_mic_monitor_get_current_mic_async (priv->mic_monitor,
265       source_output_idx, empathy_audio_src_get_current_mic_cb, self);
266 }
267
268 static GstMixerTrack *
269 empathy_audio_src_get_track (GstElement *src)
270 {
271   const GList *t;
272   GstMixerTrack *track = NULL;
273
274   if (!gst_element_implements_interface (src, GST_TYPE_MIXER))
275     {
276       g_warning ("No mixer interface implementation, can't control volume");
277       return NULL;
278     }
279
280   for (t = gst_mixer_list_tracks (GST_MIXER (src));
281       t != NULL; t = g_list_next (t))
282     {
283       GstMixerTrack *tr = t->data;
284       if (!tp_strdiff (tr->label, "Master"))
285         {
286           track = tr;
287           break;
288         }
289     }
290
291   if (track == NULL)
292     {
293       g_warning ("No suitable track found");
294     }
295   else if (track->num_channels > MAX_MIC_CHANNELS)
296     {
297       g_warning ("Microphones with more then %d channels not supported ",
298         MAX_MIC_CHANNELS);
299       track = NULL;
300     }
301
302   return track;
303 }
304
305 static GstElement *
306 create_src (void)
307 {
308   GstElement *src;
309   const gchar *description;
310
311   description = g_getenv ("EMPATHY_AUDIO_SRC");
312
313   if (description != NULL)
314     {
315       GError *error = NULL;
316
317       src = gst_parse_bin_from_description (description, TRUE, &error);
318       if (src == NULL)
319         {
320           DEBUG ("Failed to create bin %s: %s", description, error->message);
321           g_error_free (error);
322         }
323
324       return src;
325     }
326
327   /* Use pulsesrc as default */
328   src = gst_element_factory_make ("pulsesrc", NULL);
329   if (src == NULL)
330     return NULL;
331
332   empathy_call_set_stream_properties (src, TRUE);
333
334   /* Set latency (buffering on the PulseAudio side) of 20ms */
335   g_object_set (src, "buffer-time", (gint64) 20000, NULL);
336
337   return src;
338 }
339
340 static void
341 empathy_audio_src_init (EmpathyGstAudioSrc *obj)
342 {
343   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (obj);
344   GstPad *ghost, *src;
345   GstElement *capsfilter;
346   GstCaps *caps;
347
348   obj->priv = priv;
349   priv->peak_level = -G_MAXDOUBLE;
350   g_mutex_init (&priv->lock);
351
352   priv->volume = 1.0;
353
354   priv->src = create_src ();
355   if (priv->src == NULL)
356     return;
357
358   gst_bin_add (GST_BIN (obj), priv->src);
359
360   /* Explicitly state what format we want from pulsesrc. This pushes resampling
361    * and format conversion as early as possible, lowering the amount of data
362    * transferred and thus improving performance. When moving to GStreamer
363    * 0.11/1.0, this should change so that we actually request what the encoder
364    * wants downstream. */
365   caps = gst_caps_new_simple ("audio/x-raw-int",
366       "channels", G_TYPE_INT, 1,
367       "width", G_TYPE_INT, 16,
368       "depth", G_TYPE_INT, 16,
369       "rate", G_TYPE_INT, 32000,
370       NULL);
371   capsfilter = gst_element_factory_make ("capsfilter", NULL);
372   g_object_set (G_OBJECT (capsfilter), "caps", caps, NULL);
373   gst_bin_add (GST_BIN (obj), capsfilter);
374   gst_element_link (priv->src, capsfilter);
375
376   priv->level = gst_element_factory_make ("level", NULL);
377   gst_bin_add (GST_BIN (obj), priv->level);
378   gst_element_link (capsfilter, priv->level);
379
380   src = gst_element_get_static_pad (priv->level, "src");
381
382   ghost = gst_ghost_pad_new ("src", src);
383   gst_element_add_pad (GST_ELEMENT (obj), ghost);
384
385   gst_object_unref (G_OBJECT (src));
386
387   /* Listen to changes to GstPulseSrc:source-output-index so we know when
388    * it's no longer PA_INVALID_INDEX (starting for the first time) or if it
389    * changes (READY->NULL->READY...) */
390   g_signal_connect (priv->src, "notify::source-output-index",
391       G_CALLBACK (empathy_audio_src_source_output_index_notify),
392       obj);
393
394   priv->mic_monitor = empathy_mic_monitor_new ();
395   g_signal_connect (priv->mic_monitor, "microphone-changed",
396       G_CALLBACK (empathy_audio_src_microphone_changed_cb), obj);
397
398   priv->source_idx = PA_INVALID_INDEX;
399 }
400
401 static void empathy_audio_src_dispose (GObject *object);
402 static void empathy_audio_src_finalize (GObject *object);
403 static void empathy_audio_src_handle_message (GstBin *bin,
404   GstMessage *message);
405
406 static gboolean empathy_audio_src_levels_updated (gpointer user_data);
407
408 static void
409 empathy_audio_src_set_property (GObject *object,
410   guint property_id, const GValue *value, GParamSpec *pspec)
411 {
412   switch (property_id)
413     {
414       case PROP_VOLUME:
415         empathy_audio_src_set_hw_volume (EMPATHY_GST_AUDIO_SRC (object),
416           g_value_get_double (value));
417         break;
418       case PROP_MUTE:
419         empathy_audio_set_hw_mute (EMPATHY_GST_AUDIO_SRC (object),
420           g_value_get_boolean (value));
421         break;
422       default:
423         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
424     }
425 }
426
427 static void
428 empathy_audio_src_get_property (GObject *object,
429   guint property_id, GValue *value, GParamSpec *pspec)
430 {
431   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object);
432   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
433
434   switch (property_id)
435     {
436       case PROP_VOLUME:
437         g_value_set_double (value, priv->volume);
438         break;
439       case PROP_MUTE:
440         g_value_set_boolean (value, priv->mute);
441         break;
442       case PROP_PEAK_LEVEL:
443         g_mutex_lock (&priv->lock);
444         g_value_set_double (value, priv->peak_level);
445         g_mutex_unlock (&priv->lock);
446         break;
447       case PROP_RMS_LEVEL:
448         g_mutex_lock (&priv->lock);
449         g_value_set_double (value, priv->rms_level);
450         g_mutex_unlock (&priv->lock);
451         break;
452       case PROP_MICROPHONE:
453         g_value_set_uint (value, priv->source_idx);
454         break;
455       default:
456         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
457     }
458 }
459
460 static void
461 empathy_audio_src_class_init (EmpathyGstAudioSrcClass
462   *empathy_audio_src_class)
463 {
464   GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_src_class);
465   GstBinClass *gstbin_class = GST_BIN_CLASS (empathy_audio_src_class);
466   GParamSpec *param_spec;
467
468   g_type_class_add_private (empathy_audio_src_class,
469     sizeof (EmpathyGstAudioSrcPrivate));
470
471   object_class->dispose = empathy_audio_src_dispose;
472   object_class->finalize = empathy_audio_src_finalize;
473
474   object_class->set_property = empathy_audio_src_set_property;
475   object_class->get_property = empathy_audio_src_get_property;
476
477   gstbin_class->handle_message =
478     GST_DEBUG_FUNCPTR (empathy_audio_src_handle_message);
479
480   param_spec = g_param_spec_double ("volume", "Volume", "volume contol",
481     0.0, 5.0, 1.0,
482     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
483   g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
484
485   param_spec = g_param_spec_boolean ("mute", "Mute", "mute contol",
486     FALSE,
487     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
488   g_object_class_install_property (object_class, PROP_MUTE, param_spec);
489
490   param_spec = g_param_spec_double ("peak-level", "peak level", "peak level",
491     -G_MAXDOUBLE, G_MAXDOUBLE, 0,
492     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
493   g_object_class_install_property (object_class, PROP_PEAK_LEVEL, param_spec);
494
495   param_spec = g_param_spec_uint ("microphone", "microphone", "microphone",
496     0, G_MAXUINT, G_MAXUINT,
497     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
498   g_object_class_install_property (object_class, PROP_MICROPHONE, param_spec);
499
500   signals[PEAK_LEVEL_CHANGED] = g_signal_new ("peak-level-changed",
501     G_TYPE_FROM_CLASS (empathy_audio_src_class),
502     G_SIGNAL_RUN_LAST,
503     0,
504     NULL, NULL,
505     g_cclosure_marshal_generic,
506     G_TYPE_NONE, 1, G_TYPE_DOUBLE);
507
508   param_spec = g_param_spec_double ("rms-level", "RMS level", "RMS level",
509     -G_MAXDOUBLE, G_MAXDOUBLE, 0,
510     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
511   g_object_class_install_property (object_class, PROP_RMS_LEVEL, param_spec);
512
513   signals[RMS_LEVEL_CHANGED] = g_signal_new ("rms-level-changed",
514     G_TYPE_FROM_CLASS (empathy_audio_src_class),
515     G_SIGNAL_RUN_LAST,
516     0,
517     NULL, NULL,
518     g_cclosure_marshal_generic,
519     G_TYPE_NONE, 1, G_TYPE_DOUBLE);
520 }
521
522 void
523 empathy_audio_src_dispose (GObject *object)
524 {
525   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object);
526   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
527
528   if (priv->dispose_has_run)
529     return;
530
531   priv->dispose_has_run = TRUE;
532
533   if (priv->level_idle_id != 0)
534     g_source_remove (priv->level_idle_id);
535   priv->level_idle_id = 0;
536
537   if (priv->volume_idle_id != 0)
538     g_source_remove (priv->volume_idle_id);
539   priv->volume_idle_id = 0;
540
541   tp_clear_object (&priv->mic_monitor);
542
543   /* release any references held by the object here */
544
545   if (G_OBJECT_CLASS (empathy_audio_src_parent_class)->dispose)
546     G_OBJECT_CLASS (empathy_audio_src_parent_class)->dispose (object);
547 }
548
549 void
550 empathy_audio_src_finalize (GObject *object)
551 {
552   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object);
553   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
554
555   /* free any data held directly by the object here */
556   g_mutex_clear (&priv->lock);
557
558   G_OBJECT_CLASS (empathy_audio_src_parent_class)->finalize (object);
559 }
560
561 static gboolean
562 empathy_audio_src_levels_updated (gpointer user_data)
563 {
564   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
565   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
566
567   g_mutex_lock (&priv->lock);
568
569   g_signal_emit (self, signals[PEAK_LEVEL_CHANGED], 0, priv->peak_level);
570   g_signal_emit (self, signals[RMS_LEVEL_CHANGED], 0, priv->rms_level);
571   priv->level_idle_id = 0;
572
573   g_mutex_unlock (&priv->lock);
574
575   return FALSE;
576 }
577
578 static gboolean
579 empathy_audio_src_volume_changed (gpointer user_data)
580 {
581   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
582   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
583   gdouble volume;
584   gboolean mute;
585
586   g_mutex_lock (&priv->lock);
587   priv->volume_idle_id = 0;
588   g_mutex_unlock (&priv->lock);
589
590   volume = empathy_audio_src_get_hw_volume (self);
591
592   if (volume != priv->volume)
593     {
594       priv->volume = volume;
595       g_object_notify (G_OBJECT (self), "volume");
596     }
597
598   mute = empathy_audio_src_get_hw_mute (self);
599   if (mute != priv->mute)
600     {
601       priv->mute = mute;
602       g_object_notify (G_OBJECT (self), "mute");
603     }
604
605   return FALSE;
606 }
607
608 static void
609 empathy_audio_src_handle_message (GstBin *bin, GstMessage *message)
610 {
611   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (bin);
612   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
613
614   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT &&
615       GST_MESSAGE_SRC (message) == GST_OBJECT (priv->level))
616     {
617       const GstStructure *s;
618       const gchar *name;
619       const GValue *list;
620       guint i, len;
621       gdouble peak = -G_MAXDOUBLE;
622       gdouble rms = -G_MAXDOUBLE;
623
624       s = gst_message_get_structure (message);
625       name = gst_structure_get_name (s);
626
627       if (g_strcmp0 ("level", name) != 0)
628         goto out;
629
630       list = gst_structure_get_value (s, "peak");
631       len = gst_value_list_get_size (list);
632
633       for (i =0 ; i < len; i++)
634         {
635           const GValue *value;
636           gdouble db;
637
638           value = gst_value_list_get_value (list, i);
639           db = g_value_get_double (value);
640           peak = MAX (db, peak);
641         }
642
643       list = gst_structure_get_value (s, "rms");
644       len = gst_value_list_get_size (list);
645
646       for (i =0 ; i < len; i++)
647         {
648           const GValue *value;
649           gdouble db;
650
651           value = gst_value_list_get_value (list, i);
652           db = g_value_get_double (value);
653           rms = MAX (db, rms);
654         }
655
656       g_mutex_lock (&priv->lock);
657
658       priv->peak_level = peak;
659       priv->rms_level = rms;
660       if (priv->level_idle_id == 0)
661         priv->level_idle_id = g_idle_add (
662           empathy_audio_src_levels_updated, self);
663
664       g_mutex_unlock (&priv->lock);
665     }
666   else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT &&
667         GST_MESSAGE_SRC (message) == GST_OBJECT (priv->src))
668     {
669       GstMixerTrack *track = NULL;
670
671       /* Listen for mute or volume changes on the src element */
672       if (gst_mixer_message_get_type (message) ==
673           GST_MIXER_MESSAGE_VOLUME_CHANGED)
674         gst_mixer_message_parse_volume_changed (message, &track,
675             NULL, NULL);
676
677       if (gst_mixer_message_get_type (message) ==
678           GST_MIXER_MESSAGE_MUTE_TOGGLED)
679         gst_mixer_message_parse_mute_toggled (message, &track, NULL);
680
681       g_mutex_lock (&priv->lock);
682
683       if (track != NULL && track == priv->track && priv->volume_idle_id == 0)
684         priv->volume_idle_id = g_idle_add (
685             empathy_audio_src_volume_changed, self);
686
687       g_mutex_unlock (&priv->lock);
688     }
689   else if  (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STATE_CHANGED &&
690       GST_MESSAGE_SRC (message) == GST_OBJECT (priv->src))
691     {
692       GstState old, new;
693
694       gst_message_parse_state_changed (message, &old, &new, NULL);
695
696       /* GstMixer is only available in state >= READY, so only start
697        * controlling the source element when going to ready state and stop
698        * doing so when going below ready. Furthermore once we have mixer read
699        * the current volume level from it and remove the settings done by
700        * Empathy. We want to pick up the level pulseaudio saved */
701       if (old == GST_STATE_NULL && new == GST_STATE_READY)
702         {
703           g_mutex_lock (&priv->lock);
704           priv->track = empathy_audio_src_get_track (priv->src);
705           if (priv->track != NULL)
706             priv->volume_idle_id = g_idle_add (
707               empathy_audio_src_volume_changed, self);
708           g_mutex_unlock (&priv->lock);
709         }
710       else if (old == GST_STATE_READY && new == GST_STATE_NULL)
711         {
712           g_mutex_lock (&priv->lock);
713           priv->track = NULL;
714           g_mutex_unlock (&priv->lock);
715         }
716     }
717
718 out:
719    GST_BIN_CLASS (empathy_audio_src_parent_class)->handle_message (bin,
720     message);
721 }
722
723 GstElement *
724 empathy_audio_src_new (void)
725 {
726   static gboolean registered = FALSE;
727
728   if (!registered) {
729     if (!gst_element_register (NULL, "empathyaudiosrc",
730             GST_RANK_NONE, EMPATHY_TYPE_GST_AUDIO_SRC))
731       return NULL;
732     registered = TRUE;
733   }
734   return gst_element_factory_make ("empathyaudiosrc", NULL);
735 }
736
737 void
738 empathy_audio_src_set_echo_cancel (EmpathyGstAudioSrc *src,
739   gboolean enable)
740 {
741   DEBUG ("Src echo cancellation setting: %s", enable ? "on" : "off");
742   empathy_call_set_stream_properties (src->priv->src, enable);
743 }
744
745 void
746 empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume)
747 {
748   g_object_set (src, "volume", volume, NULL);
749 }
750
751 gdouble
752 empathy_audio_src_get_volume (EmpathyGstAudioSrc *src)
753 {
754   return src->priv->volume;
755 }
756
757 guint
758 empathy_audio_src_get_microphone (EmpathyGstAudioSrc *src)
759 {
760   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
761
762   return priv->source_idx;
763 }
764
765 static void
766 empathy_audio_src_change_microphone_cb (GObject *source_object,
767     GAsyncResult *result,
768     gpointer user_data)
769 {
770   EmpathyMicMonitor *monitor = EMPATHY_MIC_MONITOR (source_object);
771   GSimpleAsyncResult *simple = user_data;
772   GError *error = NULL;
773
774   if (!empathy_mic_monitor_change_microphone_finish (monitor,
775           result, &error))
776     {
777       g_simple_async_result_take_error (simple, error);
778     }
779
780   g_simple_async_result_complete (simple);
781   g_object_unref (simple);
782 }
783
784 void
785 empathy_audio_src_change_microphone_async (EmpathyGstAudioSrc *src,
786     guint microphone,
787     GAsyncReadyCallback callback,
788     gpointer user_data)
789 {
790   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
791   guint source_output_idx;
792   GSimpleAsyncResult *simple;
793
794   simple = g_simple_async_result_new (G_OBJECT (src), callback, user_data,
795       empathy_audio_src_change_microphone_async);
796
797   if (!empathy_audio_src_supports_changing_mic (src))
798     {
799       g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
800           "pulsesrc is not new enough to support changing microphone");
801       g_simple_async_result_complete_in_idle (simple);
802       g_object_unref (simple);
803       return;
804     }
805
806   source_output_idx = empathy_audio_src_get_mic_index (src);
807
808   if (source_output_idx == PA_INVALID_INDEX)
809     {
810       g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
811           "pulsesrc is not yet PLAYING");
812       g_simple_async_result_complete_in_idle (simple);
813       g_object_unref (simple);
814       return;
815     }
816
817   empathy_mic_monitor_change_microphone_async (priv->mic_monitor,
818       source_output_idx, microphone, empathy_audio_src_change_microphone_cb,
819       simple);
820 }
821
822 gboolean
823 empathy_audio_src_change_microphone_finish (EmpathyGstAudioSrc *src,
824     GAsyncResult *result,
825     GError **error)
826 {
827   empathy_implement_finish_void (src,
828       empathy_audio_src_change_microphone_async);
829 }