• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <assert.h>
12 
13 #include "webrtc/modules/audio_device/linux/audio_mixer_manager_pulse_linux.h"
14 #include "webrtc/system_wrappers/interface/trace.h"
15 
16 extern webrtc_adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable;
17 
18 // Accesses Pulse functions through our late-binding symbol table instead of
19 // directly. This way we don't have to link to libpulse, which means our binary
20 // will work on systems that don't have it.
21 #define LATE(sym) \
22   LATESYM_GET(webrtc_adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, sym)
23 
24 namespace webrtc
25 {
26 
27 enum { kMaxRetryOnFailure = 2 };
28 
AudioMixerManagerLinuxPulse(const int32_t id)29 AudioMixerManagerLinuxPulse::AudioMixerManagerLinuxPulse(const int32_t id) :
30     _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
31     _id(id),
32     _paOutputDeviceIndex(-1),
33     _paInputDeviceIndex(-1),
34     _paPlayStream(NULL),
35     _paRecStream(NULL),
36     _paMainloop(NULL),
37     _paContext(NULL),
38     _paVolume(0),
39     _paMute(0),
40     _paVolSteps(0),
41     _paSpeakerMute(false),
42     _paSpeakerVolume(PA_VOLUME_NORM),
43     _paChannels(0),
44     _paObjectsSet(false),
45     _callbackValues(false)
46 {
47     WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
48                  "%s constructed", __FUNCTION__);
49 }
50 
~AudioMixerManagerLinuxPulse()51 AudioMixerManagerLinuxPulse::~AudioMixerManagerLinuxPulse()
52 {
53     WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
54                  "%s destructed", __FUNCTION__);
55 
56     Close();
57 
58     delete &_critSect;
59 }
60 
61 // ============================================================================
62 //                                    PUBLIC METHODS
63 // ============================================================================
64 
SetPulseAudioObjects(pa_threaded_mainloop * mainloop,pa_context * context)65 int32_t AudioMixerManagerLinuxPulse::SetPulseAudioObjects(
66     pa_threaded_mainloop* mainloop,
67     pa_context* context)
68 {
69     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
70                  __FUNCTION__);
71 
72     CriticalSectionScoped lock(&_critSect);
73 
74     if (!mainloop || !context)
75     {
76         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
77                      "  could not set PulseAudio objects for mixer");
78         return -1;
79     }
80 
81     _paMainloop = mainloop;
82     _paContext = context;
83     _paObjectsSet = true;
84 
85     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
86                  "  the PulseAudio objects for the mixer has been set");
87 
88     return 0;
89 }
90 
Close()91 int32_t AudioMixerManagerLinuxPulse::Close()
92 {
93     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
94                  __FUNCTION__);
95 
96     CriticalSectionScoped lock(&_critSect);
97 
98     CloseSpeaker();
99     CloseMicrophone();
100 
101     _paMainloop = NULL;
102     _paContext = NULL;
103     _paObjectsSet = false;
104 
105     return 0;
106 
107 }
108 
CloseSpeaker()109 int32_t AudioMixerManagerLinuxPulse::CloseSpeaker()
110 {
111     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
112                  __FUNCTION__);
113 
114     CriticalSectionScoped lock(&_critSect);
115 
116     // Reset the index to -1
117     _paOutputDeviceIndex = -1;
118     _paPlayStream = NULL;
119 
120     return 0;
121 }
122 
CloseMicrophone()123 int32_t AudioMixerManagerLinuxPulse::CloseMicrophone()
124 {
125     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
126                  __FUNCTION__);
127 
128     CriticalSectionScoped lock(&_critSect);
129 
130     // Reset the index to -1
131     _paInputDeviceIndex = -1;
132     _paRecStream = NULL;
133 
134     return 0;
135 }
136 
SetPlayStream(pa_stream * playStream)137 int32_t AudioMixerManagerLinuxPulse::SetPlayStream(pa_stream* playStream)
138 {
139     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
140                  "AudioMixerManagerLinuxPulse::SetPlayStream(playStream)");
141 
142     CriticalSectionScoped lock(&_critSect);
143     _paPlayStream = playStream;
144     return 0;
145 }
146 
SetRecStream(pa_stream * recStream)147 int32_t AudioMixerManagerLinuxPulse::SetRecStream(pa_stream* recStream)
148 {
149     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
150                  "AudioMixerManagerLinuxPulse::SetRecStream(recStream)");
151 
152     CriticalSectionScoped lock(&_critSect);
153     _paRecStream = recStream;
154     return 0;
155 }
156 
OpenSpeaker(uint16_t deviceIndex)157 int32_t AudioMixerManagerLinuxPulse::OpenSpeaker(
158     uint16_t deviceIndex)
159 {
160     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
161                  "AudioMixerManagerLinuxPulse::OpenSpeaker(deviceIndex=%d)",
162                  deviceIndex);
163 
164     CriticalSectionScoped lock(&_critSect);
165 
166     // No point in opening the speaker
167     // if PA objects have not been set
168     if (!_paObjectsSet)
169     {
170         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
171                      "  PulseAudio objects has not been set");
172         return -1;
173     }
174 
175     // Set the index for the PulseAudio
176     // output device to control
177     _paOutputDeviceIndex = deviceIndex;
178 
179     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
180                  "  the output mixer device is now open");
181 
182     return 0;
183 }
184 
OpenMicrophone(uint16_t deviceIndex)185 int32_t AudioMixerManagerLinuxPulse::OpenMicrophone(
186     uint16_t deviceIndex)
187 {
188     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
189                  "AudioMixerManagerLinuxPulse::OpenMicrophone(deviceIndex=%d)",
190                  deviceIndex);
191 
192     CriticalSectionScoped lock(&_critSect);
193 
194     // No point in opening the microphone
195     // if PA objects have not been set
196     if (!_paObjectsSet)
197     {
198         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
199                      "  PulseAudio objects have not been set");
200         return -1;
201     }
202 
203     // Set the index for the PulseAudio
204     // input device to control
205     _paInputDeviceIndex = deviceIndex;
206 
207     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
208                  "  the input mixer device is now open");
209 
210     return 0;
211 }
212 
SpeakerIsInitialized() const213 bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const
214 {
215     WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s",
216                  __FUNCTION__);
217 
218     return (_paOutputDeviceIndex != -1);
219 }
220 
MicrophoneIsInitialized() const221 bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const
222 {
223     WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s",
224                  __FUNCTION__);
225 
226     return (_paInputDeviceIndex != -1);
227 }
228 
SetSpeakerVolume(uint32_t volume)229 int32_t AudioMixerManagerLinuxPulse::SetSpeakerVolume(
230     uint32_t volume)
231 {
232     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
233                  "AudioMixerManagerLinuxPulse::SetSpeakerVolume(volume=%u)",
234                  volume);
235 
236     CriticalSectionScoped lock(&_critSect);
237 
238     if (_paOutputDeviceIndex == -1)
239     {
240         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
241                      "  output device index has not been set");
242         return -1;
243     }
244 
245     bool setFailed(false);
246 
247     if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
248         != PA_STREAM_UNCONNECTED))
249     {
250         // We can only really set the volume if we have a connected stream
251         PaLock();
252 
253         // Get the number of channels from the sample specification
254         const pa_sample_spec *spec =
255             LATE(pa_stream_get_sample_spec)(_paPlayStream);
256         if (!spec)
257         {
258             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
259                          "  could not get sample specification");
260             PaUnLock();
261             return -1;
262         }
263 
264         // Set the same volume for all channels
265         pa_cvolume cVolumes;
266         LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume);
267 
268         pa_operation* paOperation = NULL;
269         paOperation = LATE(pa_context_set_sink_input_volume)(
270             _paContext,
271             LATE(pa_stream_get_index)(_paPlayStream),
272             &cVolumes,
273             PaSetVolumeCallback, NULL);
274         if (!paOperation)
275         {
276             setFailed = true;
277         }
278 
279         // Don't need to wait for the completion
280         LATE(pa_operation_unref)(paOperation);
281 
282         PaUnLock();
283     } else
284     {
285         // We have not created a stream or it's not connected to the sink
286         // Save the volume to be set at connection
287         _paSpeakerVolume = volume;
288     }
289 
290     if (setFailed)
291     {
292         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
293                      " could not set speaker volume, error%d",
294                      LATE(pa_context_errno)(_paContext));
295 
296         return -1;
297     }
298 
299     return 0;
300 }
301 
302 int32_t
SpeakerVolume(uint32_t & volume) const303 AudioMixerManagerLinuxPulse::SpeakerVolume(uint32_t& volume) const
304 {
305 
306     if (_paOutputDeviceIndex == -1)
307     {
308         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
309                      "  output device index has not been set");
310         return -1;
311     }
312 
313     if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
314         != PA_STREAM_UNCONNECTED))
315     {
316         // We can only get the volume if we have a connected stream
317         if (!GetSinkInputInfo())
318           return -1;
319 
320         volume = static_cast<uint32_t> (_paVolume);
321         ResetCallbackVariables();
322     } else
323     {
324         volume = _paSpeakerVolume;
325     }
326 
327     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
328                  "     AudioMixerManagerLinuxPulse::SpeakerVolume() => vol=%i",
329                  volume);
330 
331     return 0;
332 }
333 
334 int32_t
MaxSpeakerVolume(uint32_t & maxVolume) const335 AudioMixerManagerLinuxPulse::MaxSpeakerVolume(uint32_t& maxVolume) const
336 {
337 
338     if (_paOutputDeviceIndex == -1)
339     {
340         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
341                      "  output device index has not been set");
342         return -1;
343     }
344 
345     // PA_VOLUME_NORM corresponds to 100% (0db)
346     // but PA allows up to 150 db amplification
347     maxVolume = static_cast<uint32_t> (PA_VOLUME_NORM);
348 
349     return 0;
350 }
351 
352 int32_t
MinSpeakerVolume(uint32_t & minVolume) const353 AudioMixerManagerLinuxPulse::MinSpeakerVolume(uint32_t& minVolume) const
354 {
355 
356     if (_paOutputDeviceIndex == -1)
357     {
358         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
359                      "  output device index has not been set");
360         return -1;
361     }
362 
363     minVolume = static_cast<uint32_t> (PA_VOLUME_MUTED);
364 
365     return 0;
366 }
367 
368 int32_t
SpeakerVolumeStepSize(uint16_t & stepSize) const369 AudioMixerManagerLinuxPulse::SpeakerVolumeStepSize(uint16_t& stepSize) const
370 {
371 
372     if (_paOutputDeviceIndex == -1)
373     {
374         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
375                      "  output device index has not been set");
376         return -1;
377     }
378 
379     // The sink input (stream) will always have step size = 1
380     // There are PA_VOLUME_NORM+1 steps
381     stepSize = 1;
382 
383     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
384                  "     AudioMixerManagerLinuxPulse::SpeakerVolumeStepSize() => "
385                  "size=%i, stepSize");
386 
387     // Reset members modified by callback
388     ResetCallbackVariables();
389 
390     return 0;
391 }
392 
393 int32_t
SpeakerVolumeIsAvailable(bool & available)394 AudioMixerManagerLinuxPulse::SpeakerVolumeIsAvailable(bool& available)
395 {
396     if (_paOutputDeviceIndex == -1)
397     {
398         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
399                      "  output device index has not been set");
400         return -1;
401     }
402 
403     // Always available in Pulse Audio
404     available = true;
405 
406     return 0;
407 }
408 
409 int32_t
SpeakerMuteIsAvailable(bool & available)410 AudioMixerManagerLinuxPulse::SpeakerMuteIsAvailable(bool& available)
411 {
412     if (_paOutputDeviceIndex == -1)
413     {
414         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
415                      "  output device index has not been set");
416         return -1;
417     }
418 
419     // Always available in Pulse Audio
420     available = true;
421 
422     return 0;
423 }
424 
SetSpeakerMute(bool enable)425 int32_t AudioMixerManagerLinuxPulse::SetSpeakerMute(bool enable)
426 {
427     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
428                  "AudioMixerManagerLinuxPulse::SetSpeakerMute(enable=%u)",
429                  enable);
430 
431     CriticalSectionScoped lock(&_critSect);
432 
433     if (_paOutputDeviceIndex == -1)
434     {
435         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
436                      "  output device index has not been set");
437         return -1;
438     }
439 
440     bool setFailed(false);
441 
442     if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
443         != PA_STREAM_UNCONNECTED))
444     {
445         // We can only really mute if we have a connected stream
446         PaLock();
447 
448         pa_operation* paOperation = NULL;
449         paOperation = LATE(pa_context_set_sink_input_mute)(
450             _paContext,
451             LATE(pa_stream_get_index)(_paPlayStream),
452             (int) enable,
453             PaSetVolumeCallback,
454             NULL);
455         if (!paOperation)
456         {
457             setFailed = true;
458         }
459 
460         // Don't need to wait for the completion
461         LATE(pa_operation_unref)(paOperation);
462 
463         PaUnLock();
464     } else
465     {
466         // We have not created a stream or it's not connected to the sink
467         // Save the mute status to be set at connection
468         _paSpeakerMute = enable;
469     }
470 
471     if (setFailed)
472     {
473         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
474                      " could not mute speaker, error%d",
475                      LATE(pa_context_errno)(_paContext));
476         return -1;
477     }
478 
479     return 0;
480 }
481 
SpeakerMute(bool & enabled) const482 int32_t AudioMixerManagerLinuxPulse::SpeakerMute(bool& enabled) const
483 {
484 
485     if (_paOutputDeviceIndex == -1)
486     {
487         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
488                      "  output device index has not been set");
489         return -1;
490     }
491 
492     if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
493         != PA_STREAM_UNCONNECTED))
494     {
495         // We can only get the mute status if we have a connected stream
496         if (!GetSinkInputInfo())
497           return -1;
498 
499         enabled = static_cast<bool> (_paMute);
500         ResetCallbackVariables();
501     } else
502     {
503         enabled = _paSpeakerMute;
504     }
505 
506     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
507                  "     AudioMixerManagerLinuxPulse::SpeakerMute() => "
508                  "enabled=%i, enabled");
509 
510     return 0;
511 }
512 
513 int32_t
StereoPlayoutIsAvailable(bool & available)514 AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable(bool& available)
515 {
516     if (_paOutputDeviceIndex == -1)
517     {
518         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
519                      "  output device index has not been set");
520         return -1;
521     }
522 
523     uint32_t deviceIndex = (uint32_t) _paOutputDeviceIndex;
524 
525     PaLock();
526 
527     // Get the actual stream device index if we have a connected stream
528     // The device used by the stream can be changed
529     // during the call
530     if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
531         != PA_STREAM_UNCONNECTED))
532     {
533         deviceIndex = LATE(pa_stream_get_device_index)(_paPlayStream);
534     }
535 
536     PaUnLock();
537 
538     if (!GetSinkInfoByIndex(deviceIndex))
539       return -1;
540 
541     available = static_cast<bool> (_paChannels == 2);
542 
543     // Reset members modified by callback
544     ResetCallbackVariables();
545 
546     return 0;
547 }
548 
549 int32_t
StereoRecordingIsAvailable(bool & available)550 AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable(bool& available)
551 {
552     if (_paInputDeviceIndex == -1)
553     {
554         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
555                      "  input device index has not been set");
556         return -1;
557     }
558 
559     uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
560 
561     PaLock();
562 
563     // Get the actual stream device index if we have a connected stream
564     // The device used by the stream can be changed
565     // during the call
566     if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
567         != PA_STREAM_UNCONNECTED))
568     {
569         deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
570     }
571 
572     pa_operation* paOperation = NULL;
573     ResetCallbackVariables();
574 
575     // Get info for this source
576     // We want to know if the actual device can record in stereo
577     paOperation = LATE(pa_context_get_source_info_by_index)(
578         _paContext, deviceIndex,
579         PaSourceInfoCallback,
580         (void*) this);
581 
582     WaitForOperationCompletion(paOperation);
583     PaUnLock();
584 
585     if (!_callbackValues)
586     {
587         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
588                      "Error getting number of input channels: %d",
589                      LATE(pa_context_errno)(_paContext));
590         return -1;
591     }
592 
593     available = static_cast<bool> (_paChannels == 2);
594 
595     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
596                  "     AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable()"
597                  " => available=%i, available");
598 
599     // Reset members modified by callback
600     ResetCallbackVariables();
601 
602     return 0;
603 }
604 
MicrophoneMuteIsAvailable(bool & available)605 int32_t AudioMixerManagerLinuxPulse::MicrophoneMuteIsAvailable(
606     bool& available)
607 {
608     if (_paInputDeviceIndex == -1)
609     {
610         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
611                      "  input device index has not been set");
612         return -1;
613     }
614 
615     // Always available in Pulse Audio
616     available = true;
617 
618     return 0;
619 }
620 
SetMicrophoneMute(bool enable)621 int32_t AudioMixerManagerLinuxPulse::SetMicrophoneMute(bool enable)
622 {
623     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
624                  "AudioMixerManagerLinuxPulse::SetMicrophoneMute(enable=%u)",
625                  enable);
626 
627     CriticalSectionScoped lock(&_critSect);
628 
629     if (_paInputDeviceIndex == -1)
630     {
631         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
632                      "  input device index has not been set");
633         return -1;
634     }
635 
636     bool setFailed(false);
637     pa_operation* paOperation = NULL;
638     ResetCallbackVariables();
639 
640     uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
641 
642     PaLock();
643 
644     // Get the actual stream device index if we have a connected stream
645     // The device used by the stream can be changed
646     // during the call
647     if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
648         != PA_STREAM_UNCONNECTED))
649     {
650         deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
651     }
652 
653     // Set mute switch for the source
654     paOperation = LATE(pa_context_set_source_mute_by_index)(
655         _paContext, deviceIndex,
656         enable,
657         PaSetVolumeCallback, NULL);
658 
659     if (!paOperation)
660     {
661         setFailed = true;
662     }
663 
664     // Don't need to wait for this to complete.
665     LATE(pa_operation_unref)(paOperation);
666 
667     PaUnLock();
668 
669     // Reset variables altered by callback
670     ResetCallbackVariables();
671 
672     if (setFailed)
673     {
674         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
675                      " could not mute microphone, error%d",
676                      LATE(pa_context_errno)(_paContext));
677         return -1;
678     }
679 
680     return 0;
681 }
682 
MicrophoneMute(bool & enabled) const683 int32_t AudioMixerManagerLinuxPulse::MicrophoneMute(bool& enabled) const
684 {
685 
686     if (_paInputDeviceIndex == -1)
687     {
688         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
689                      "  input device index has not been set");
690         return -1;
691     }
692 
693     uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
694 
695     PaLock();
696 
697     // Get the actual stream device index if we have a connected stream
698     // The device used by the stream can be changed
699     // during the call
700     if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
701         != PA_STREAM_UNCONNECTED))
702     {
703         deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
704     }
705 
706     PaUnLock();
707 
708     if (!GetSourceInfoByIndex(deviceIndex))
709       return -1;
710 
711     enabled = static_cast<bool> (_paMute);
712 
713     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
714                  "     AudioMixerManagerLinuxPulse::MicrophoneMute() =>"
715                  " enabled=%i, enabled");
716 
717     // Reset members modified by callback
718     ResetCallbackVariables();
719 
720     return 0;
721 }
722 
723 int32_t
MicrophoneBoostIsAvailable(bool & available)724 AudioMixerManagerLinuxPulse::MicrophoneBoostIsAvailable(bool& available)
725 {
726     if (_paInputDeviceIndex == -1)
727     {
728         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
729                      "  input device index has not been set");
730         return -1;
731     }
732 
733     // Always unavailable in Pulse Audio
734     // Could make it possible to use PA_VOLUME_MAX
735     // but that gives bad audio with some sound cards
736     available = false;
737 
738     return 0;
739 }
740 
SetMicrophoneBoost(bool enable)741 int32_t AudioMixerManagerLinuxPulse::SetMicrophoneBoost(bool enable)
742 {
743     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
744                  "AudioMixerManagerLinuxPulse::SetMicrophoneBoost(enable=%u)",
745                  enable);
746 
747     CriticalSectionScoped lock(&_critSect);
748 
749     if (_paInputDeviceIndex == -1)
750     {
751         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
752                      "  input device index has not been set");
753         return -1;
754     }
755 
756     // Ensure that the selected microphone destination has a valid boost control
757     bool available(false);
758     MicrophoneBoostIsAvailable(available);
759     if (!available)
760     {
761         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
762                      "  it is not possible to enable microphone boost");
763         return -1;
764     }
765 
766     // It is assumed that the call above fails!
767 
768     return 0;
769 }
770 
MicrophoneBoost(bool & enabled) const771 int32_t AudioMixerManagerLinuxPulse::MicrophoneBoost(bool& enabled) const
772 {
773 
774     if (_paInputDeviceIndex == -1)
775     {
776         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
777                      "  input device index has not been set");
778         return -1;
779     }
780 
781     // Microphone boost cannot be enabled on this platform!
782     enabled = false;
783 
784     return 0;
785 }
786 
MicrophoneVolumeIsAvailable(bool & available)787 int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeIsAvailable(
788     bool& available)
789 {
790     if (_paInputDeviceIndex == -1)
791     {
792         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
793                      "  input device index has not been set");
794         return -1;
795     }
796 
797     // Always available in Pulse Audio
798     available = true;
799 
800     return 0;
801 }
802 
803 int32_t
SetMicrophoneVolume(uint32_t volume)804 AudioMixerManagerLinuxPulse::SetMicrophoneVolume(uint32_t volume)
805 {
806     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
807                  "AudioMixerManagerLinuxPulse::SetMicrophoneVolume(volume=%u)",
808                  volume);
809 
810     CriticalSectionScoped lock(&_critSect);
811 
812     if (_paInputDeviceIndex == -1)
813     {
814         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
815                      "  input device index has not been set");
816         return -1;
817     }
818 
819     // Unlike output streams, input streams have no concept of a stream volume,
820     // only a device volume. So we have to change the volume of the device
821     // itself.
822 
823     // The device may have a different number of channels than the stream and
824     // their mapping may be different, so we don't want to use the channel count
825     // from our sample spec. We could use PA_CHANNELS_MAX to cover our bases,
826     // and the server allows that even if the device's channel count is lower,
827     // but some buggy PA clients don't like that (the pavucontrol on Hardy dies
828     // in an assert if the channel count is different). So instead we look up
829     // the actual number of channels that the device has.
830 
831     uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
832 
833     PaLock();
834 
835     // Get the actual stream device index if we have a connected stream
836     // The device used by the stream can be changed
837     // during the call
838     if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
839         != PA_STREAM_UNCONNECTED))
840     {
841         deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
842     }
843 
844     bool setFailed(false);
845     pa_operation* paOperation = NULL;
846     ResetCallbackVariables();
847 
848     // Get the number of channels for this source
849     paOperation
850         = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex,
851                                                     PaSourceInfoCallback,
852                                                     (void*) this);
853 
854     WaitForOperationCompletion(paOperation);
855 
856     if (!_callbackValues)
857     {
858         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
859                      "Error getting input channels: %d",
860                      LATE(pa_context_errno)(_paContext));
861         PaUnLock();
862         return -1;
863     }
864 
865     uint8_t channels = _paChannels;
866     ResetCallbackVariables();
867 
868     pa_cvolume cVolumes;
869     LATE(pa_cvolume_set)(&cVolumes, channels, volume);
870 
871     // Set the volume for the source
872     paOperation
873         = LATE(pa_context_set_source_volume_by_index)(_paContext, deviceIndex,
874                                                       &cVolumes,
875                                                       PaSetVolumeCallback, NULL);
876 
877     if (!paOperation)
878     {
879         setFailed = true;
880     }
881 
882     // Don't need to wait for this to complete.
883     LATE(pa_operation_unref)(paOperation);
884 
885     PaUnLock();
886 
887     // Reset variables altered by callback
888     ResetCallbackVariables();
889 
890     if (setFailed)
891     {
892         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
893                      " could not set microphone volume, error%d",
894                      LATE(pa_context_errno)(_paContext));
895         return -1;
896     }
897 
898     return 0;
899 }
900 
901 int32_t
MicrophoneVolume(uint32_t & volume) const902 AudioMixerManagerLinuxPulse::MicrophoneVolume(uint32_t& volume) const
903 {
904 
905     if (_paInputDeviceIndex == -1)
906     {
907         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
908                      "  input device index has not been set");
909         return -1;
910     }
911 
912     uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
913 
914     PaLock();
915 
916     // Get the actual stream device index if we have a connected stream
917     // The device used by the stream can be changed
918     // during the call
919     if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
920         != PA_STREAM_UNCONNECTED))
921     {
922         deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
923     }
924 
925     PaUnLock();
926 
927     if (!GetSourceInfoByIndex(deviceIndex))
928       return -1;
929 
930     volume = static_cast<uint32_t> (_paVolume);
931 
932     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
933                  "     AudioMixerManagerLinuxPulse::MicrophoneVolume() => vol=%i, volume");
934 
935     // Reset members modified by callback
936     ResetCallbackVariables();
937 
938     return 0;
939 }
940 
941 int32_t
MaxMicrophoneVolume(uint32_t & maxVolume) const942 AudioMixerManagerLinuxPulse::MaxMicrophoneVolume(uint32_t& maxVolume) const
943 {
944 
945     if (_paInputDeviceIndex == -1)
946     {
947         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
948                      "  input device index has not been set");
949         return -1;
950     }
951 
952     // PA_VOLUME_NORM corresponds to 100% (0db)
953     // PA allows up to 150 db amplification (PA_VOLUME_MAX)
954     // but that doesn't work well for all sound cards
955     maxVolume = static_cast<uint32_t> (PA_VOLUME_NORM);
956 
957     return 0;
958 }
959 
960 int32_t
MinMicrophoneVolume(uint32_t & minVolume) const961 AudioMixerManagerLinuxPulse::MinMicrophoneVolume(uint32_t& minVolume) const
962 {
963 
964     if (_paInputDeviceIndex == -1)
965     {
966         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
967                      "  input device index has not been set");
968         return -1;
969     }
970 
971     minVolume = static_cast<uint32_t> (PA_VOLUME_MUTED);
972 
973     return 0;
974 }
975 
MicrophoneVolumeStepSize(uint16_t & stepSize) const976 int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize(
977     uint16_t& stepSize) const
978 {
979 
980     if (_paInputDeviceIndex == -1)
981     {
982         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
983                      "  input device index has not been set");
984         return -1;
985     }
986 
987     uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
988 
989     PaLock();
990 
991     // Get the actual stream device index if we have a connected stream
992     // The device used by the stream can be changed
993     // during the call
994     if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
995         != PA_STREAM_UNCONNECTED))
996     {
997         deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
998     }
999 
1000     pa_operation* paOperation = NULL;
1001     ResetCallbackVariables();
1002 
1003     // Get info for this source
1004     paOperation
1005         = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex,
1006                                                     PaSourceInfoCallback,
1007                                                     (void*) this);
1008 
1009     WaitForOperationCompletion(paOperation);
1010 
1011     PaUnLock();
1012 
1013     if (!_callbackValues)
1014     {
1015         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1016                      "Error getting step size: %d",
1017                      LATE(pa_context_errno)(_paContext));
1018         return -1;
1019     }
1020 
1021     stepSize = static_cast<uint16_t> ((PA_VOLUME_NORM + 1) / _paVolSteps);
1022 
1023     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1024                  "     AudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize()"
1025                  " => size=%i, stepSize");
1026 
1027     // Reset members modified by callback
1028     ResetCallbackVariables();
1029 
1030     return 0;
1031 }
1032 
1033 // ============================================================================
1034 //                                 Private Methods
1035 // ============================================================================
1036 
PaSinkInfoCallback(pa_context *,const pa_sink_info * i,int eol,void * pThis)1037 void AudioMixerManagerLinuxPulse::PaSinkInfoCallback(pa_context */*c*/,
1038                                                      const pa_sink_info *i,
1039                                                      int eol, void *pThis)
1040 {
1041     static_cast<AudioMixerManagerLinuxPulse*> (pThis)-> PaSinkInfoCallbackHandler(
1042         i, eol);
1043 }
1044 
PaSinkInputInfoCallback(pa_context *,const pa_sink_input_info * i,int eol,void * pThis)1045 void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallback(
1046     pa_context */*c*/,
1047     const pa_sink_input_info *i,
1048     int eol, void *pThis)
1049 {
1050     static_cast<AudioMixerManagerLinuxPulse*> (pThis)->
1051         PaSinkInputInfoCallbackHandler(i, eol);
1052 }
1053 
1054 
PaSourceInfoCallback(pa_context *,const pa_source_info * i,int eol,void * pThis)1055 void AudioMixerManagerLinuxPulse::PaSourceInfoCallback(pa_context */*c*/,
1056                                                        const pa_source_info *i,
1057                                                        int eol, void *pThis)
1058 {
1059     static_cast<AudioMixerManagerLinuxPulse*> (pThis)->
1060         PaSourceInfoCallbackHandler(i, eol);
1061 }
1062 
PaSetVolumeCallback(pa_context * c,int success,void *)1063 void AudioMixerManagerLinuxPulse::PaSetVolumeCallback(pa_context * c,
1064                                                       int success, void */*pThis*/)
1065 {
1066     if (!success)
1067     {
1068         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1,
1069                      " failed to set volume");
1070     }
1071 }
1072 
PaSinkInfoCallbackHandler(const pa_sink_info * i,int eol)1073 void AudioMixerManagerLinuxPulse::PaSinkInfoCallbackHandler(
1074     const pa_sink_info *i,
1075     int eol)
1076 {
1077     if (eol)
1078     {
1079         // Signal that we are done
1080         LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
1081         return;
1082     }
1083 
1084     _callbackValues = true;
1085     _paChannels = i->channel_map.channels; // Get number of channels
1086     pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
1087     for (int j = 0; j < _paChannels; ++j)
1088     {
1089         if (paVolume < i->volume.values[j])
1090         {
1091             paVolume = i->volume.values[j];
1092         }
1093     }
1094     _paVolume = paVolume; // get the max volume for any channel
1095     _paMute = i->mute; // get mute status
1096 
1097     // supported since PA 0.9.15
1098     //_paVolSteps = i->n_volume_steps; // get the number of volume steps
1099     // default value is PA_VOLUME_NORM+1
1100     _paVolSteps = PA_VOLUME_NORM + 1;
1101 }
1102 
PaSinkInputInfoCallbackHandler(const pa_sink_input_info * i,int eol)1103 void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallbackHandler(
1104     const pa_sink_input_info *i,
1105     int eol)
1106 {
1107     if (eol)
1108     {
1109         // Signal that we are done
1110         LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
1111         return;
1112     }
1113 
1114     _callbackValues = true;
1115     _paChannels = i->channel_map.channels; // Get number of channels
1116     pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
1117     for (int j = 0; j < _paChannels; ++j)
1118     {
1119         if (paVolume < i->volume.values[j])
1120         {
1121             paVolume = i->volume.values[j];
1122         }
1123     }
1124     _paVolume = paVolume; // Get the max volume for any channel
1125     _paMute = i->mute; // Get mute status
1126 }
1127 
PaSourceInfoCallbackHandler(const pa_source_info * i,int eol)1128 void AudioMixerManagerLinuxPulse::PaSourceInfoCallbackHandler(
1129     const pa_source_info *i,
1130     int eol)
1131 {
1132     if (eol)
1133     {
1134         // Signal that we are done
1135         LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
1136         return;
1137     }
1138 
1139     _callbackValues = true;
1140     _paChannels = i->channel_map.channels; // Get number of channels
1141     pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
1142     for (int j = 0; j < _paChannels; ++j)
1143     {
1144         if (paVolume < i->volume.values[j])
1145         {
1146             paVolume = i->volume.values[j];
1147         }
1148     }
1149     _paVolume = paVolume; // Get the max volume for any channel
1150     _paMute = i->mute; // Get mute status
1151 
1152     // supported since PA 0.9.15
1153     //_paVolSteps = i->n_volume_steps; // Get the number of volume steps
1154     // default value is PA_VOLUME_NORM+1
1155     _paVolSteps = PA_VOLUME_NORM + 1;
1156 }
1157 
ResetCallbackVariables() const1158 void AudioMixerManagerLinuxPulse::ResetCallbackVariables() const
1159 {
1160     _paVolume = 0;
1161     _paMute = 0;
1162     _paVolSteps = 0;
1163     _paChannels = 0;
1164     _callbackValues = false;
1165 }
1166 
WaitForOperationCompletion(pa_operation * paOperation) const1167 void AudioMixerManagerLinuxPulse::WaitForOperationCompletion(
1168     pa_operation* paOperation) const
1169 {
1170     while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING)
1171     {
1172         LATE(pa_threaded_mainloop_wait)(_paMainloop);
1173     }
1174 
1175     LATE(pa_operation_unref)(paOperation);
1176 }
1177 
PaLock() const1178 void AudioMixerManagerLinuxPulse::PaLock() const
1179 {
1180     LATE(pa_threaded_mainloop_lock)(_paMainloop);
1181 }
1182 
PaUnLock() const1183 void AudioMixerManagerLinuxPulse::PaUnLock() const
1184 {
1185     LATE(pa_threaded_mainloop_unlock)(_paMainloop);
1186 }
1187 
GetSinkInputInfo() const1188 bool AudioMixerManagerLinuxPulse::GetSinkInputInfo() const {
1189   pa_operation* paOperation = NULL;
1190   ResetCallbackVariables();
1191 
1192   PaLock();
1193   for (int retries = 0; retries < kMaxRetryOnFailure && !_callbackValues;
1194        retries ++) {
1195     // Get info for this stream (sink input).
1196     paOperation = LATE(pa_context_get_sink_input_info)(
1197         _paContext,
1198         LATE(pa_stream_get_index)(_paPlayStream),
1199         PaSinkInputInfoCallback,
1200         (void*) this);
1201 
1202     WaitForOperationCompletion(paOperation);
1203   }
1204   PaUnLock();
1205 
1206   if (!_callbackValues) {
1207     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1208                  "GetSinkInputInfo failed to get volume info : %d",
1209                  LATE(pa_context_errno)(_paContext));
1210     return false;
1211   }
1212 
1213   return true;
1214 }
1215 
GetSinkInfoByIndex(int device_index) const1216 bool AudioMixerManagerLinuxPulse::GetSinkInfoByIndex(
1217     int device_index) const {
1218   pa_operation* paOperation = NULL;
1219   ResetCallbackVariables();
1220 
1221   PaLock();
1222   for (int retries = 0; retries < kMaxRetryOnFailure && !_callbackValues;
1223        retries ++) {
1224     paOperation = LATE(pa_context_get_sink_info_by_index)(_paContext,
1225         device_index, PaSinkInfoCallback, (void*) this);
1226 
1227     WaitForOperationCompletion(paOperation);
1228   }
1229   PaUnLock();
1230 
1231   if (!_callbackValues) {
1232     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1233                  "GetSinkInfoByIndex failed to get volume info: %d",
1234                  LATE(pa_context_errno)(_paContext));
1235     return false;
1236   }
1237 
1238   return true;
1239 }
1240 
GetSourceInfoByIndex(int device_index) const1241 bool AudioMixerManagerLinuxPulse::GetSourceInfoByIndex(
1242     int device_index) const {
1243   pa_operation* paOperation = NULL;
1244   ResetCallbackVariables();
1245 
1246   PaLock();
1247   for (int retries = 0; retries < kMaxRetryOnFailure && !_callbackValues;
1248        retries ++) {
1249   paOperation  = LATE(pa_context_get_source_info_by_index)(
1250       _paContext, device_index, PaSourceInfoCallback, (void*) this);
1251 
1252   WaitForOperationCompletion(paOperation);
1253   }
1254 
1255   PaUnLock();
1256 
1257   if (!_callbackValues) {
1258     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1259                  "GetSourceInfoByIndex error: %d",
1260                  LATE(pa_context_errno)(_paContext));
1261     return false;
1262   }
1263 
1264   return true;
1265 }
1266 
1267 }
1268