]> git.0d.be Git - empathy.git/blob - src/empathy-audio-src.c
Move should_create_salut_account to local-xmpp-assistant-widget
[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   priv->lock = g_mutex_new ();
351   priv->volume = 1.0;
352
353   priv->src = create_src ();
354   if (priv->src == NULL)
355     return;
356
357   gst_bin_add (GST_BIN (obj), priv->src);
358
359   /* Explicitly state what format we want from pulsesrc. This pushes resampling
360    * and format conversion as early as possible, lowering the amount of data
361    * transferred and thus improving performance. When moving to GStreamer
362    * 0.11/1.0, this should change so that we actually request what the encoder
363    * wants downstream. */
364   caps = gst_caps_new_simple ("audio/x-raw-int",
365       "channels", G_TYPE_INT, 1,
366       "width", G_TYPE_INT, 16,
367       "depth", G_TYPE_INT, 16,
368       "rate", G_TYPE_INT, 32000,
369       NULL);
370   capsfilter = gst_element_factory_make ("capsfilter", NULL);
371   g_object_set (G_OBJECT (capsfilter), "caps", caps, NULL);
372   gst_bin_add (GST_BIN (obj), capsfilter);
373   gst_element_link (priv->src, capsfilter);
374
375   priv->level = gst_element_factory_make ("level", NULL);
376   gst_bin_add (GST_BIN (obj), priv->level);
377   gst_element_link (capsfilter, priv->level);
378
379   src = gst_element_get_static_pad (priv->level, "src");
380
381   ghost = gst_ghost_pad_new ("src", src);
382   gst_element_add_pad (GST_ELEMENT (obj), ghost);
383
384   gst_object_unref (G_OBJECT (src));
385
386   /* Listen to changes to GstPulseSrc:source-output-index so we know when
387    * it's no longer PA_INVALID_INDEX (starting for the first time) or if it
388    * changes (READY->NULL->READY...) */
389   g_signal_connect (priv->src, "notify::source-output-index",
390       G_CALLBACK (empathy_audio_src_source_output_index_notify),
391       obj);
392
393   priv->mic_monitor = empathy_mic_monitor_new ();
394   g_signal_connect (priv->mic_monitor, "microphone-changed",
395       G_CALLBACK (empathy_audio_src_microphone_changed_cb), obj);
396
397   priv->source_idx = PA_INVALID_INDEX;
398 }
399
400 static void empathy_audio_src_dispose (GObject *object);
401 static void empathy_audio_src_finalize (GObject *object);
402 static void empathy_audio_src_handle_message (GstBin *bin,
403   GstMessage *message);
404
405 static gboolean empathy_audio_src_levels_updated (gpointer user_data);
406
407 static void
408 empathy_audio_src_set_property (GObject *object,
409   guint property_id, const GValue *value, GParamSpec *pspec)
410 {
411   switch (property_id)
412     {
413       case PROP_VOLUME:
414         empathy_audio_src_set_hw_volume (EMPATHY_GST_AUDIO_SRC (object),
415           g_value_get_double (value));
416         break;
417       case PROP_MUTE:
418         empathy_audio_set_hw_mute (EMPATHY_GST_AUDIO_SRC (object),
419           g_value_get_boolean (value));
420         break;
421       default:
422         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
423     }
424 }
425
426 static void
427 empathy_audio_src_get_property (GObject *object,
428   guint property_id, GValue *value, GParamSpec *pspec)
429 {
430   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object);
431   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
432
433   switch (property_id)
434     {
435       case PROP_VOLUME:
436         g_value_set_double (value, priv->volume);
437         break;
438       case PROP_MUTE:
439         g_value_set_boolean (value, priv->mute);
440         break;
441       case PROP_PEAK_LEVEL:
442         g_mutex_lock (priv->lock);
443         g_value_set_double (value, priv->peak_level);
444         g_mutex_unlock (priv->lock);
445         break;
446       case PROP_RMS_LEVEL:
447         g_mutex_lock (priv->lock);
448         g_value_set_double (value, priv->rms_level);
449         g_mutex_unlock (priv->lock);
450         break;
451       case PROP_MICROPHONE:
452         g_value_set_uint (value, priv->source_idx);
453         break;
454       default:
455         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
456     }
457 }
458
459 static void
460 empathy_audio_src_class_init (EmpathyGstAudioSrcClass
461   *empathy_audio_src_class)
462 {
463   GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_src_class);
464   GstBinClass *gstbin_class = GST_BIN_CLASS (empathy_audio_src_class);
465   GParamSpec *param_spec;
466
467   g_type_class_add_private (empathy_audio_src_class,
468     sizeof (EmpathyGstAudioSrcPrivate));
469
470   object_class->dispose = empathy_audio_src_dispose;
471   object_class->finalize = empathy_audio_src_finalize;
472
473   object_class->set_property = empathy_audio_src_set_property;
474   object_class->get_property = empathy_audio_src_get_property;
475
476   gstbin_class->handle_message =
477     GST_DEBUG_FUNCPTR (empathy_audio_src_handle_message);
478
479   param_spec = g_param_spec_double ("volume", "Volume", "volume contol",
480     0.0, 5.0, 1.0,
481     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
482   g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
483
484   param_spec = g_param_spec_boolean ("mute", "Mute", "mute contol",
485     FALSE,
486     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
487   g_object_class_install_property (object_class, PROP_MUTE, param_spec);
488
489   param_spec = g_param_spec_double ("peak-level", "peak level", "peak level",
490     -G_MAXDOUBLE, G_MAXDOUBLE, 0,
491     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
492   g_object_class_install_property (object_class, PROP_PEAK_LEVEL, param_spec);
493
494   param_spec = g_param_spec_uint ("microphone", "microphone", "microphone",
495     0, G_MAXUINT, G_MAXUINT,
496     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
497   g_object_class_install_property (object_class, PROP_MICROPHONE, param_spec);
498
499   signals[PEAK_LEVEL_CHANGED] = g_signal_new ("peak-level-changed",
500     G_TYPE_FROM_CLASS (empathy_audio_src_class),
501     G_SIGNAL_RUN_LAST,
502     0,
503     NULL, NULL,
504     g_cclosure_marshal_generic,
505     G_TYPE_NONE, 1, G_TYPE_DOUBLE);
506
507   param_spec = g_param_spec_double ("rms-level", "RMS level", "RMS level",
508     -G_MAXDOUBLE, G_MAXDOUBLE, 0,
509     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
510   g_object_class_install_property (object_class, PROP_RMS_LEVEL, param_spec);
511
512   signals[RMS_LEVEL_CHANGED] = g_signal_new ("rms-level-changed",
513     G_TYPE_FROM_CLASS (empathy_audio_src_class),
514     G_SIGNAL_RUN_LAST,
515     0,
516     NULL, NULL,
517     g_cclosure_marshal_generic,
518     G_TYPE_NONE, 1, G_TYPE_DOUBLE);
519 }
520
521 void
522 empathy_audio_src_dispose (GObject *object)
523 {
524   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object);
525   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
526
527   if (priv->dispose_has_run)
528     return;
529
530   priv->dispose_has_run = TRUE;
531
532   if (priv->level_idle_id != 0)
533     g_source_remove (priv->level_idle_id);
534   priv->level_idle_id = 0;
535
536   if (priv->volume_idle_id != 0)
537     g_source_remove (priv->volume_idle_id);
538   priv->volume_idle_id = 0;
539
540   tp_clear_object (&priv->mic_monitor);
541
542   /* release any references held by the object here */
543
544   if (G_OBJECT_CLASS (empathy_audio_src_parent_class)->dispose)
545     G_OBJECT_CLASS (empathy_audio_src_parent_class)->dispose (object);
546 }
547
548 void
549 empathy_audio_src_finalize (GObject *object)
550 {
551   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object);
552   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
553
554   /* free any data held directly by the object here */
555   g_mutex_free (priv->lock);
556
557   G_OBJECT_CLASS (empathy_audio_src_parent_class)->finalize (object);
558 }
559
560 static gboolean
561 empathy_audio_src_levels_updated (gpointer user_data)
562 {
563   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
564   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
565
566   g_mutex_lock (priv->lock);
567
568   g_signal_emit (self, signals[PEAK_LEVEL_CHANGED], 0, priv->peak_level);
569   g_signal_emit (self, signals[RMS_LEVEL_CHANGED], 0, priv->rms_level);
570   priv->level_idle_id = 0;
571
572   g_mutex_unlock (priv->lock);
573
574   return FALSE;
575 }
576
577 static gboolean
578 empathy_audio_src_volume_changed (gpointer user_data)
579 {
580   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
581   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
582   gdouble volume;
583   gboolean mute;
584
585   g_mutex_lock (priv->lock);
586   priv->volume_idle_id = 0;
587   g_mutex_unlock (priv->lock);
588
589   volume = empathy_audio_src_get_hw_volume (self);
590
591   if (volume != priv->volume)
592     {
593       priv->volume = volume;
594       g_object_notify (G_OBJECT (self), "volume");
595     }
596
597   mute = empathy_audio_src_get_hw_mute (self);
598   if (mute != priv->mute)
599     {
600       priv->mute = mute;
601       g_object_notify (G_OBJECT (self), "mute");
602     }
603
604   return FALSE;
605 }
606
607 static void
608 empathy_audio_src_handle_message (GstBin *bin, GstMessage *message)
609 {
610   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (bin);
611   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
612
613   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT &&
614       GST_MESSAGE_SRC (message) == GST_OBJECT (priv->level))
615     {
616       const GstStructure *s;
617       const gchar *name;
618       const GValue *list;
619       guint i, len;
620       gdouble peak = -G_MAXDOUBLE;
621       gdouble rms = -G_MAXDOUBLE;
622
623       s = gst_message_get_structure (message);
624       name = gst_structure_get_name (s);
625
626       if (g_strcmp0 ("level", name) != 0)
627         goto out;
628
629       list = gst_structure_get_value (s, "peak");
630       len = gst_value_list_get_size (list);
631
632       for (i =0 ; i < len; i++)
633         {
634           const GValue *value;
635           gdouble db;
636
637           value = gst_value_list_get_value (list, i);
638           db = g_value_get_double (value);
639           peak = MAX (db, peak);
640         }
641
642       list = gst_structure_get_value (s, "rms");
643       len = gst_value_list_get_size (list);
644
645       for (i =0 ; i < len; i++)
646         {
647           const GValue *value;
648           gdouble db;
649
650           value = gst_value_list_get_value (list, i);
651           db = g_value_get_double (value);
652           rms = MAX (db, rms);
653         }
654
655       g_mutex_lock (priv->lock);
656
657       priv->peak_level = peak;
658       priv->rms_level = rms;
659       if (priv->level_idle_id == 0)
660         priv->level_idle_id = g_idle_add (
661           empathy_audio_src_levels_updated, self);
662
663       g_mutex_unlock (priv->lock);
664     }
665   else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT &&
666         GST_MESSAGE_SRC (message) == GST_OBJECT (priv->src))
667     {
668       GstMixerTrack *track = NULL;
669
670       /* Listen for mute or volume changes on the src element */
671       if (gst_mixer_message_get_type (message) ==
672           GST_MIXER_MESSAGE_VOLUME_CHANGED)
673         gst_mixer_message_parse_volume_changed (message, &track,
674             NULL, NULL);
675
676       if (gst_mixer_message_get_type (message) ==
677           GST_MIXER_MESSAGE_MUTE_TOGGLED)
678         gst_mixer_message_parse_mute_toggled (message, &track, NULL);
679
680       g_mutex_lock (priv->lock);
681
682       if (track != NULL && track == priv->track && priv->volume_idle_id == 0)
683         priv->volume_idle_id = g_idle_add (
684             empathy_audio_src_volume_changed, self);
685
686       g_mutex_unlock (priv->lock);
687     }
688   else if  (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STATE_CHANGED &&
689       GST_MESSAGE_SRC (message) == GST_OBJECT (priv->src))
690     {
691       GstState old, new;
692
693       gst_message_parse_state_changed (message, &old, &new, NULL);
694
695       /* GstMixer is only available in state >= READY, so only start
696        * controlling the source element when going to ready state and stop
697        * doing so when going below ready. Furthermore once we have mixer read
698        * the current volume level from it and remove the settings done by
699        * Empathy. We want to pick up the level pulseaudio saved */
700       if (old == GST_STATE_NULL && new == GST_STATE_READY)
701         {
702           g_mutex_lock (priv->lock);
703           priv->track = empathy_audio_src_get_track (priv->src);
704           if (priv->track != NULL)
705             priv->volume_idle_id = g_idle_add (
706               empathy_audio_src_volume_changed, self);
707           g_mutex_unlock (priv->lock);
708         }
709       else if (old == GST_STATE_READY && new == GST_STATE_NULL)
710         {
711           g_mutex_lock (priv->lock);
712           priv->track = NULL;
713           g_mutex_unlock (priv->lock);
714         }
715     }
716
717 out:
718    GST_BIN_CLASS (empathy_audio_src_parent_class)->handle_message (bin,
719     message);
720 }
721
722 GstElement *
723 empathy_audio_src_new (void)
724 {
725   static gboolean registered = FALSE;
726
727   if (!registered) {
728     if (!gst_element_register (NULL, "empathyaudiosrc",
729             GST_RANK_NONE, EMPATHY_TYPE_GST_AUDIO_SRC))
730       return NULL;
731     registered = TRUE;
732   }
733   return gst_element_factory_make ("empathyaudiosrc", NULL);
734 }
735
736 void
737 empathy_audio_src_set_echo_cancel (EmpathyGstAudioSrc *src,
738   gboolean enable)
739 {
740   DEBUG ("Src echo cancellation setting: %s", enable ? "on" : "off");
741   empathy_call_set_stream_properties (src->priv->src, enable);
742 }
743
744 void
745 empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume)
746 {
747   g_object_set (src, "volume", volume, NULL);
748 }
749
750 gdouble
751 empathy_audio_src_get_volume (EmpathyGstAudioSrc *src)
752 {
753   return src->priv->volume;
754 }
755
756 guint
757 empathy_audio_src_get_microphone (EmpathyGstAudioSrc *src)
758 {
759   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
760
761   return priv->source_idx;
762 }
763
764 static void
765 empathy_audio_src_change_microphone_cb (GObject *source_object,
766     GAsyncResult *result,
767     gpointer user_data)
768 {
769   EmpathyMicMonitor *monitor = EMPATHY_MIC_MONITOR (source_object);
770   GSimpleAsyncResult *simple = user_data;
771   GError *error = NULL;
772
773   if (!empathy_mic_monitor_change_microphone_finish (monitor,
774           result, &error))
775     {
776       g_simple_async_result_take_error (simple, error);
777     }
778
779   g_simple_async_result_complete (simple);
780   g_object_unref (simple);
781 }
782
783 void
784 empathy_audio_src_change_microphone_async (EmpathyGstAudioSrc *src,
785     guint microphone,
786     GAsyncReadyCallback callback,
787     gpointer user_data)
788 {
789   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
790   guint source_output_idx;
791   GSimpleAsyncResult *simple;
792
793   simple = g_simple_async_result_new (G_OBJECT (src), callback, user_data,
794       empathy_audio_src_change_microphone_async);
795
796   if (!empathy_audio_src_supports_changing_mic (src))
797     {
798       g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
799           "pulsesrc is not new enough to support changing microphone");
800       g_simple_async_result_complete_in_idle (simple);
801       g_object_unref (simple);
802       return;
803     }
804
805   source_output_idx = empathy_audio_src_get_mic_index (src);
806
807   if (source_output_idx == PA_INVALID_INDEX)
808     {
809       g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
810           "pulsesrc is not yet PLAYING");
811       g_simple_async_result_complete_in_idle (simple);
812       g_object_unref (simple);
813       return;
814     }
815
816   empathy_mic_monitor_change_microphone_async (priv->mic_monitor,
817       source_output_idx, microphone, empathy_audio_src_change_microphone_cb,
818       simple);
819 }
820
821 gboolean
822 empathy_audio_src_change_microphone_finish (EmpathyGstAudioSrc *src,
823     GAsyncResult *result,
824     GError **error)
825 {
826   empathy_implement_finish_void (src,
827       empathy_audio_src_change_microphone_async);
828 }