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 "modules/audio_device/linux/audio_mixer_manager_pulse_linux.h"
14 #include "rtc_base/checks.h"
15 #include "rtc_base/logging.h"
16 
17 extern webrtc::adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable;
18 
19 // Accesses Pulse functions through our late-binding symbol table instead of
20 // directly. This way we don't have to link to libpulse, which means our
21 // binary will work on systems that don't have it.
22 #define LATE(sym)                                                             \
23   LATESYM_GET(webrtc::adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, \
24               sym)
25 
26 namespace webrtc {
27 
28 class AutoPulseLock {
29  public:
AutoPulseLock(pa_threaded_mainloop * pa_mainloop)30   explicit AutoPulseLock(pa_threaded_mainloop* pa_mainloop)
31       : pa_mainloop_(pa_mainloop) {
32     LATE(pa_threaded_mainloop_lock)(pa_mainloop_);
33   }
34 
~AutoPulseLock()35   ~AutoPulseLock() { LATE(pa_threaded_mainloop_unlock)(pa_mainloop_); }
36 
37  private:
38   pa_threaded_mainloop* const pa_mainloop_;
39 };
40 
AudioMixerManagerLinuxPulse()41 AudioMixerManagerLinuxPulse::AudioMixerManagerLinuxPulse()
42     : _paOutputDeviceIndex(-1),
43       _paInputDeviceIndex(-1),
44       _paPlayStream(NULL),
45       _paRecStream(NULL),
46       _paMainloop(NULL),
47       _paContext(NULL),
48       _paVolume(0),
49       _paMute(0),
50       _paVolSteps(0),
51       _paSpeakerMute(false),
52       _paSpeakerVolume(PA_VOLUME_NORM),
53       _paChannels(0),
54       _paObjectsSet(false) {
55   RTC_LOG(LS_INFO) << __FUNCTION__ << " created";
56 }
57 
~AudioMixerManagerLinuxPulse()58 AudioMixerManagerLinuxPulse::~AudioMixerManagerLinuxPulse() {
59   RTC_DCHECK(thread_checker_.CalledOnValidThread());
60   RTC_LOG(LS_INFO) << __FUNCTION__ << " destroyed";
61 
62   Close();
63 }
64 
65 // ===========================================================================
66 //                                    PUBLIC METHODS
67 // ===========================================================================
68 
SetPulseAudioObjects(pa_threaded_mainloop * mainloop,pa_context * context)69 int32_t AudioMixerManagerLinuxPulse::SetPulseAudioObjects(
70     pa_threaded_mainloop* mainloop,
71     pa_context* context) {
72   RTC_DCHECK(thread_checker_.CalledOnValidThread());
73   RTC_LOG(LS_VERBOSE) << __FUNCTION__;
74 
75   if (!mainloop || !context) {
76     RTC_LOG(LS_ERROR) << "could not set PulseAudio objects for mixer";
77     return -1;
78   }
79 
80   _paMainloop = mainloop;
81   _paContext = context;
82   _paObjectsSet = true;
83 
84   RTC_LOG(LS_VERBOSE) << "the PulseAudio objects for the mixer has been set";
85 
86   return 0;
87 }
88 
Close()89 int32_t AudioMixerManagerLinuxPulse::Close() {
90   RTC_DCHECK(thread_checker_.CalledOnValidThread());
91   RTC_LOG(LS_VERBOSE) << __FUNCTION__;
92 
93   CloseSpeaker();
94   CloseMicrophone();
95 
96   _paMainloop = NULL;
97   _paContext = NULL;
98   _paObjectsSet = false;
99 
100   return 0;
101 }
102 
CloseSpeaker()103 int32_t AudioMixerManagerLinuxPulse::CloseSpeaker() {
104   RTC_DCHECK(thread_checker_.CalledOnValidThread());
105   RTC_LOG(LS_VERBOSE) << __FUNCTION__;
106 
107   // Reset the index to -1
108   _paOutputDeviceIndex = -1;
109   _paPlayStream = NULL;
110 
111   return 0;
112 }
113 
CloseMicrophone()114 int32_t AudioMixerManagerLinuxPulse::CloseMicrophone() {
115   RTC_DCHECK(thread_checker_.CalledOnValidThread());
116   RTC_LOG(LS_VERBOSE) << __FUNCTION__;
117 
118   // Reset the index to -1
119   _paInputDeviceIndex = -1;
120   _paRecStream = NULL;
121 
122   return 0;
123 }
124 
SetPlayStream(pa_stream * playStream)125 int32_t AudioMixerManagerLinuxPulse::SetPlayStream(pa_stream* playStream) {
126   RTC_DCHECK(thread_checker_.CalledOnValidThread());
127   RTC_LOG(LS_VERBOSE)
128       << "AudioMixerManagerLinuxPulse::SetPlayStream(playStream)";
129 
130   _paPlayStream = playStream;
131   return 0;
132 }
133 
SetRecStream(pa_stream * recStream)134 int32_t AudioMixerManagerLinuxPulse::SetRecStream(pa_stream* recStream) {
135   RTC_DCHECK(thread_checker_.CalledOnValidThread());
136   RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetRecStream(recStream)";
137 
138   _paRecStream = recStream;
139   return 0;
140 }
141 
OpenSpeaker(uint16_t deviceIndex)142 int32_t AudioMixerManagerLinuxPulse::OpenSpeaker(uint16_t deviceIndex) {
143   RTC_DCHECK(thread_checker_.CalledOnValidThread());
144   RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::OpenSpeaker(deviceIndex="
145                       << deviceIndex << ")";
146 
147   // No point in opening the speaker
148   // if PA objects have not been set
149   if (!_paObjectsSet) {
150     RTC_LOG(LS_ERROR) << "PulseAudio objects has not been set";
151     return -1;
152   }
153 
154   // Set the index for the PulseAudio
155   // output device to control
156   _paOutputDeviceIndex = deviceIndex;
157 
158   RTC_LOG(LS_VERBOSE) << "the output mixer device is now open";
159 
160   return 0;
161 }
162 
OpenMicrophone(uint16_t deviceIndex)163 int32_t AudioMixerManagerLinuxPulse::OpenMicrophone(uint16_t deviceIndex) {
164   RTC_DCHECK(thread_checker_.CalledOnValidThread());
165   RTC_LOG(LS_VERBOSE)
166       << "AudioMixerManagerLinuxPulse::OpenMicrophone(deviceIndex="
167       << deviceIndex << ")";
168 
169   // No point in opening the microphone
170   // if PA objects have not been set
171   if (!_paObjectsSet) {
172     RTC_LOG(LS_ERROR) << "PulseAudio objects have not been set";
173     return -1;
174   }
175 
176   // Set the index for the PulseAudio
177   // input device to control
178   _paInputDeviceIndex = deviceIndex;
179 
180   RTC_LOG(LS_VERBOSE) << "the input mixer device is now open";
181 
182   return 0;
183 }
184 
SpeakerIsInitialized() const185 bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const {
186   RTC_DCHECK(thread_checker_.CalledOnValidThread());
187   RTC_LOG(LS_INFO) << __FUNCTION__;
188 
189   return (_paOutputDeviceIndex != -1);
190 }
191 
MicrophoneIsInitialized() const192 bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const {
193   RTC_DCHECK(thread_checker_.CalledOnValidThread());
194   RTC_LOG(LS_INFO) << __FUNCTION__;
195 
196   return (_paInputDeviceIndex != -1);
197 }
198 
SetSpeakerVolume(uint32_t volume)199 int32_t AudioMixerManagerLinuxPulse::SetSpeakerVolume(uint32_t volume) {
200   RTC_DCHECK(thread_checker_.CalledOnValidThread());
201   RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetSpeakerVolume(volume="
202                       << volume << ")";
203 
204   if (_paOutputDeviceIndex == -1) {
205     RTC_LOG(LS_WARNING) << "output device index has not been set";
206     return -1;
207   }
208 
209   bool setFailed(false);
210 
211   if (_paPlayStream &&
212       (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
213     // We can only really set the volume if we have a connected stream
214     AutoPulseLock auto_lock(_paMainloop);
215 
216     // Get the number of channels from the sample specification
217     const pa_sample_spec* spec = LATE(pa_stream_get_sample_spec)(_paPlayStream);
218     if (!spec) {
219       RTC_LOG(LS_ERROR) << "could not get sample specification";
220       return -1;
221     }
222 
223     // Set the same volume for all channels
224     pa_cvolume cVolumes;
225     LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume);
226 
227     pa_operation* paOperation = NULL;
228     paOperation = LATE(pa_context_set_sink_input_volume)(
229         _paContext, LATE(pa_stream_get_index)(_paPlayStream), &cVolumes,
230         PaSetVolumeCallback, NULL);
231     if (!paOperation) {
232       setFailed = true;
233     }
234 
235     // Don't need to wait for the completion
236     LATE(pa_operation_unref)(paOperation);
237   } else {
238     // We have not created a stream or it's not connected to the sink
239     // Save the volume to be set at connection
240     _paSpeakerVolume = volume;
241   }
242 
243   if (setFailed) {
244     RTC_LOG(LS_WARNING) << "could not set speaker volume, error="
245                         << LATE(pa_context_errno)(_paContext);
246 
247     return -1;
248   }
249 
250   return 0;
251 }
252 
SpeakerVolume(uint32_t & volume) const253 int32_t AudioMixerManagerLinuxPulse::SpeakerVolume(uint32_t& volume) const {
254   if (_paOutputDeviceIndex == -1) {
255     RTC_LOG(LS_WARNING) << "output device index has not been set";
256     return -1;
257   }
258 
259   if (_paPlayStream &&
260       (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
261     // We can only get the volume if we have a connected stream
262     if (!GetSinkInputInfo())
263       return -1;
264 
265     AutoPulseLock auto_lock(_paMainloop);
266     volume = static_cast<uint32_t>(_paVolume);
267   } else {
268     AutoPulseLock auto_lock(_paMainloop);
269     volume = _paSpeakerVolume;
270   }
271 
272   RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SpeakerVolume() => vol="
273                       << volume;
274 
275   return 0;
276 }
277 
MaxSpeakerVolume(uint32_t & maxVolume) const278 int32_t AudioMixerManagerLinuxPulse::MaxSpeakerVolume(
279     uint32_t& maxVolume) const {
280   if (_paOutputDeviceIndex == -1) {
281     RTC_LOG(LS_WARNING) << "output device index has not been set";
282     return -1;
283   }
284 
285   // PA_VOLUME_NORM corresponds to 100% (0db)
286   // but PA allows up to 150 db amplification
287   maxVolume = static_cast<uint32_t>(PA_VOLUME_NORM);
288 
289   return 0;
290 }
291 
MinSpeakerVolume(uint32_t & minVolume) const292 int32_t AudioMixerManagerLinuxPulse::MinSpeakerVolume(
293     uint32_t& minVolume) const {
294   if (_paOutputDeviceIndex == -1) {
295     RTC_LOG(LS_WARNING) << "output device index has not been set";
296     return -1;
297   }
298 
299   minVolume = static_cast<uint32_t>(PA_VOLUME_MUTED);
300 
301   return 0;
302 }
303 
SpeakerVolumeIsAvailable(bool & available)304 int32_t AudioMixerManagerLinuxPulse::SpeakerVolumeIsAvailable(bool& available) {
305   RTC_DCHECK(thread_checker_.CalledOnValidThread());
306   if (_paOutputDeviceIndex == -1) {
307     RTC_LOG(LS_WARNING) << "output device index has not been set";
308     return -1;
309   }
310 
311   // Always available in Pulse Audio
312   available = true;
313 
314   return 0;
315 }
316 
SpeakerMuteIsAvailable(bool & available)317 int32_t AudioMixerManagerLinuxPulse::SpeakerMuteIsAvailable(bool& available) {
318   RTC_DCHECK(thread_checker_.CalledOnValidThread());
319   if (_paOutputDeviceIndex == -1) {
320     RTC_LOG(LS_WARNING) << "output device index has not been set";
321     return -1;
322   }
323 
324   // Always available in Pulse Audio
325   available = true;
326 
327   return 0;
328 }
329 
SetSpeakerMute(bool enable)330 int32_t AudioMixerManagerLinuxPulse::SetSpeakerMute(bool enable) {
331   RTC_DCHECK(thread_checker_.CalledOnValidThread());
332   RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetSpeakerMute(enable="
333                       << enable << ")";
334 
335   if (_paOutputDeviceIndex == -1) {
336     RTC_LOG(LS_WARNING) << "output device index has not been set";
337     return -1;
338   }
339 
340   bool setFailed(false);
341 
342   if (_paPlayStream &&
343       (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
344     // We can only really mute if we have a connected stream
345     AutoPulseLock auto_lock(_paMainloop);
346 
347     pa_operation* paOperation = NULL;
348     paOperation = LATE(pa_context_set_sink_input_mute)(
349         _paContext, LATE(pa_stream_get_index)(_paPlayStream), (int)enable,
350         PaSetVolumeCallback, NULL);
351     if (!paOperation) {
352       setFailed = true;
353     }
354 
355     // Don't need to wait for the completion
356     LATE(pa_operation_unref)(paOperation);
357   } else {
358     // We have not created a stream or it's not connected to the sink
359     // Save the mute status to be set at connection
360     _paSpeakerMute = enable;
361   }
362 
363   if (setFailed) {
364     RTC_LOG(LS_WARNING) << "could not mute speaker, error="
365                         << LATE(pa_context_errno)(_paContext);
366     return -1;
367   }
368 
369   return 0;
370 }
371 
SpeakerMute(bool & enabled) const372 int32_t AudioMixerManagerLinuxPulse::SpeakerMute(bool& enabled) const {
373   if (_paOutputDeviceIndex == -1) {
374     RTC_LOG(LS_WARNING) << "output device index has not been set";
375     return -1;
376   }
377 
378   if (_paPlayStream &&
379       (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
380     // We can only get the mute status if we have a connected stream
381     if (!GetSinkInputInfo())
382       return -1;
383 
384     enabled = static_cast<bool>(_paMute);
385   } else {
386     enabled = _paSpeakerMute;
387   }
388   RTC_LOG(LS_VERBOSE)
389       << "AudioMixerManagerLinuxPulse::SpeakerMute() => enabled=" << enabled;
390 
391   return 0;
392 }
393 
StereoPlayoutIsAvailable(bool & available)394 int32_t AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable(bool& available) {
395   RTC_DCHECK(thread_checker_.CalledOnValidThread());
396   if (_paOutputDeviceIndex == -1) {
397     RTC_LOG(LS_WARNING) << "output device index has not been set";
398     return -1;
399   }
400 
401   uint32_t deviceIndex = (uint32_t)_paOutputDeviceIndex;
402 
403   {
404     AutoPulseLock auto_lock(_paMainloop);
405 
406     // Get the actual stream device index if we have a connected stream
407     // The device used by the stream can be changed
408     // during the call
409     if (_paPlayStream &&
410         (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
411       deviceIndex = LATE(pa_stream_get_device_index)(_paPlayStream);
412     }
413   }
414 
415   if (!GetSinkInfoByIndex(deviceIndex))
416     return -1;
417 
418   available = static_cast<bool>(_paChannels == 2);
419 
420   return 0;
421 }
422 
StereoRecordingIsAvailable(bool & available)423 int32_t AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable(
424     bool& available) {
425   RTC_DCHECK(thread_checker_.CalledOnValidThread());
426   if (_paInputDeviceIndex == -1) {
427     RTC_LOG(LS_WARNING) << "input device index has not been set";
428     return -1;
429   }
430 
431   uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
432 
433   AutoPulseLock auto_lock(_paMainloop);
434 
435   // Get the actual stream device index if we have a connected stream
436   // The device used by the stream can be changed
437   // during the call
438   if (_paRecStream &&
439       (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
440     deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
441   }
442 
443   pa_operation* paOperation = NULL;
444 
445   // Get info for this source
446   // We want to know if the actual device can record in stereo
447   paOperation = LATE(pa_context_get_source_info_by_index)(
448       _paContext, deviceIndex, PaSourceInfoCallback, (void*)this);
449 
450   WaitForOperationCompletion(paOperation);
451 
452   available = static_cast<bool>(_paChannels == 2);
453 
454   RTC_LOG(LS_VERBOSE)
455       << "AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable()"
456       << " => available=" << available;
457 
458   return 0;
459 }
460 
MicrophoneMuteIsAvailable(bool & available)461 int32_t AudioMixerManagerLinuxPulse::MicrophoneMuteIsAvailable(
462     bool& available) {
463   RTC_DCHECK(thread_checker_.CalledOnValidThread());
464   if (_paInputDeviceIndex == -1) {
465     RTC_LOG(LS_WARNING) << "input device index has not been set";
466     return -1;
467   }
468 
469   // Always available in Pulse Audio
470   available = true;
471 
472   return 0;
473 }
474 
SetMicrophoneMute(bool enable)475 int32_t AudioMixerManagerLinuxPulse::SetMicrophoneMute(bool enable) {
476   RTC_DCHECK(thread_checker_.CalledOnValidThread());
477   RTC_LOG(LS_VERBOSE)
478       << "AudioMixerManagerLinuxPulse::SetMicrophoneMute(enable=" << enable
479       << ")";
480 
481   if (_paInputDeviceIndex == -1) {
482     RTC_LOG(LS_WARNING) << "input device index has not been set";
483     return -1;
484   }
485 
486   bool setFailed(false);
487   pa_operation* paOperation = NULL;
488 
489   uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
490 
491   AutoPulseLock auto_lock(_paMainloop);
492 
493   // Get the actual stream device index if we have a connected stream
494   // The device used by the stream can be changed
495   // during the call
496   if (_paRecStream &&
497       (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
498     deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
499   }
500 
501   // Set mute switch for the source
502   paOperation = LATE(pa_context_set_source_mute_by_index)(
503       _paContext, deviceIndex, enable, PaSetVolumeCallback, NULL);
504 
505   if (!paOperation) {
506     setFailed = true;
507   }
508 
509   // Don't need to wait for this to complete.
510   LATE(pa_operation_unref)(paOperation);
511 
512   if (setFailed) {
513     RTC_LOG(LS_WARNING) << "could not mute microphone, error="
514                         << LATE(pa_context_errno)(_paContext);
515     return -1;
516   }
517 
518   return 0;
519 }
520 
MicrophoneMute(bool & enabled) const521 int32_t AudioMixerManagerLinuxPulse::MicrophoneMute(bool& enabled) const {
522   RTC_DCHECK(thread_checker_.CalledOnValidThread());
523   if (_paInputDeviceIndex == -1) {
524     RTC_LOG(LS_WARNING) << "input device index has not been set";
525     return -1;
526   }
527 
528   uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
529 
530   {
531     AutoPulseLock auto_lock(_paMainloop);
532     // Get the actual stream device index if we have a connected stream
533     // The device used by the stream can be changed
534     // during the call
535     if (_paRecStream &&
536         (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
537       deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
538     }
539   }
540 
541   if (!GetSourceInfoByIndex(deviceIndex))
542     return -1;
543 
544   enabled = static_cast<bool>(_paMute);
545 
546   RTC_LOG(LS_VERBOSE)
547       << "AudioMixerManagerLinuxPulse::MicrophoneMute() => enabled=" << enabled;
548 
549   return 0;
550 }
551 
MicrophoneVolumeIsAvailable(bool & available)552 int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeIsAvailable(
553     bool& available) {
554   RTC_DCHECK(thread_checker_.CalledOnValidThread());
555   if (_paInputDeviceIndex == -1) {
556     RTC_LOG(LS_WARNING) << "input device index has not been set";
557     return -1;
558   }
559 
560   // Always available in Pulse Audio
561   available = true;
562 
563   return 0;
564 }
565 
SetMicrophoneVolume(uint32_t volume)566 int32_t AudioMixerManagerLinuxPulse::SetMicrophoneVolume(uint32_t volume) {
567   RTC_LOG(LS_VERBOSE)
568       << "AudioMixerManagerLinuxPulse::SetMicrophoneVolume(volume=" << volume
569       << ")";
570 
571   if (_paInputDeviceIndex == -1) {
572     RTC_LOG(LS_WARNING) << "input device index has not been set";
573     return -1;
574   }
575 
576   // Unlike output streams, input streams have no concept of a stream
577   // volume, only a device volume. So we have to change the volume of the
578   // device itself.
579 
580   // The device may have a different number of channels than the stream and
581   // their mapping may be different, so we don't want to use the channel
582   // count from our sample spec. We could use PA_CHANNELS_MAX to cover our
583   // bases, and the server allows that even if the device's channel count
584   // is lower, but some buggy PA clients don't like that (the pavucontrol
585   // on Hardy dies in an assert if the channel count is different). So
586   // instead we look up the actual number of channels that the device has.
587   AutoPulseLock auto_lock(_paMainloop);
588   uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
589 
590   // Get the actual stream device index if we have a connected stream
591   // The device used by the stream can be changed
592   // during the call
593   if (_paRecStream &&
594       (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
595     deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
596   }
597 
598   bool setFailed(false);
599   pa_operation* paOperation = NULL;
600 
601   // Get the number of channels for this source
602   paOperation = LATE(pa_context_get_source_info_by_index)(
603       _paContext, deviceIndex, PaSourceInfoCallback, (void*)this);
604 
605   WaitForOperationCompletion(paOperation);
606 
607   uint8_t channels = _paChannels;
608   pa_cvolume cVolumes;
609   LATE(pa_cvolume_set)(&cVolumes, channels, volume);
610 
611   // Set the volume for the source
612   paOperation = LATE(pa_context_set_source_volume_by_index)(
613       _paContext, deviceIndex, &cVolumes, PaSetVolumeCallback, NULL);
614 
615   if (!paOperation) {
616     setFailed = true;
617   }
618 
619   // Don't need to wait for this to complete.
620   LATE(pa_operation_unref)(paOperation);
621 
622   if (setFailed) {
623     RTC_LOG(LS_WARNING) << "could not set microphone volume, error="
624                         << LATE(pa_context_errno)(_paContext);
625     return -1;
626   }
627 
628   return 0;
629 }
630 
MicrophoneVolume(uint32_t & volume) const631 int32_t AudioMixerManagerLinuxPulse::MicrophoneVolume(uint32_t& volume) const {
632   if (_paInputDeviceIndex == -1) {
633     RTC_LOG(LS_WARNING) << "input device index has not been set";
634     return -1;
635   }
636 
637   uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
638 
639   {
640     AutoPulseLock auto_lock(_paMainloop);
641     // Get the actual stream device index if we have a connected stream.
642     // The device used by the stream can be changed during the call.
643     if (_paRecStream &&
644         (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
645       deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
646     }
647   }
648 
649   if (!GetSourceInfoByIndex(deviceIndex))
650     return -1;
651 
652   {
653     AutoPulseLock auto_lock(_paMainloop);
654     volume = static_cast<uint32_t>(_paVolume);
655   }
656 
657   RTC_LOG(LS_VERBOSE)
658       << "AudioMixerManagerLinuxPulse::MicrophoneVolume() => vol=" << volume;
659 
660   return 0;
661 }
662 
MaxMicrophoneVolume(uint32_t & maxVolume) const663 int32_t AudioMixerManagerLinuxPulse::MaxMicrophoneVolume(
664     uint32_t& maxVolume) const {
665   if (_paInputDeviceIndex == -1) {
666     RTC_LOG(LS_WARNING) << "input device index has not been set";
667     return -1;
668   }
669 
670   // PA_VOLUME_NORM corresponds to 100% (0db)
671   // PA allows up to 150 db amplification (PA_VOLUME_MAX)
672   // but that doesn't work well for all sound cards
673   maxVolume = static_cast<uint32_t>(PA_VOLUME_NORM);
674 
675   return 0;
676 }
677 
MinMicrophoneVolume(uint32_t & minVolume) const678 int32_t AudioMixerManagerLinuxPulse::MinMicrophoneVolume(
679     uint32_t& minVolume) const {
680   if (_paInputDeviceIndex == -1) {
681     RTC_LOG(LS_WARNING) << "input device index has not been set";
682     return -1;
683   }
684 
685   minVolume = static_cast<uint32_t>(PA_VOLUME_MUTED);
686 
687   return 0;
688 }
689 
690 // ===========================================================================
691 //                                 Private Methods
692 // ===========================================================================
693 
PaSinkInfoCallback(pa_context *,const pa_sink_info * i,int eol,void * pThis)694 void AudioMixerManagerLinuxPulse::PaSinkInfoCallback(pa_context* /*c*/,
695                                                      const pa_sink_info* i,
696                                                      int eol,
697                                                      void* pThis) {
698   static_cast<AudioMixerManagerLinuxPulse*>(pThis)->PaSinkInfoCallbackHandler(
699       i, eol);
700 }
701 
PaSinkInputInfoCallback(pa_context *,const pa_sink_input_info * i,int eol,void * pThis)702 void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallback(
703     pa_context* /*c*/,
704     const pa_sink_input_info* i,
705     int eol,
706     void* pThis) {
707   static_cast<AudioMixerManagerLinuxPulse*>(pThis)
708       ->PaSinkInputInfoCallbackHandler(i, eol);
709 }
710 
PaSourceInfoCallback(pa_context *,const pa_source_info * i,int eol,void * pThis)711 void AudioMixerManagerLinuxPulse::PaSourceInfoCallback(pa_context* /*c*/,
712                                                        const pa_source_info* i,
713                                                        int eol,
714                                                        void* pThis) {
715   static_cast<AudioMixerManagerLinuxPulse*>(pThis)->PaSourceInfoCallbackHandler(
716       i, eol);
717 }
718 
PaSetVolumeCallback(pa_context * c,int success,void *)719 void AudioMixerManagerLinuxPulse::PaSetVolumeCallback(pa_context* c,
720                                                       int success,
721                                                       void* /*pThis*/) {
722   if (!success) {
723     RTC_LOG(LS_ERROR) << "failed to set volume";
724   }
725 }
726 
PaSinkInfoCallbackHandler(const pa_sink_info * i,int eol)727 void AudioMixerManagerLinuxPulse::PaSinkInfoCallbackHandler(
728     const pa_sink_info* i,
729     int eol) {
730   if (eol) {
731     // Signal that we are done
732     LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
733     return;
734   }
735 
736   _paChannels = i->channel_map.channels;   // Get number of channels
737   pa_volume_t paVolume = PA_VOLUME_MUTED;  // Minimum possible value.
738   for (int j = 0; j < _paChannels; ++j) {
739     if (paVolume < i->volume.values[j]) {
740       paVolume = i->volume.values[j];
741     }
742   }
743   _paVolume = paVolume;  // get the max volume for any channel
744   _paMute = i->mute;     // get mute status
745 
746   // supported since PA 0.9.15
747   //_paVolSteps = i->n_volume_steps; // get the number of volume steps
748   // default value is PA_VOLUME_NORM+1
749   _paVolSteps = PA_VOLUME_NORM + 1;
750 }
751 
PaSinkInputInfoCallbackHandler(const pa_sink_input_info * i,int eol)752 void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallbackHandler(
753     const pa_sink_input_info* i,
754     int eol) {
755   if (eol) {
756     // Signal that we are done
757     LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
758     return;
759   }
760 
761   _paChannels = i->channel_map.channels;   // Get number of channels
762   pa_volume_t paVolume = PA_VOLUME_MUTED;  // Minimum possible value.
763   for (int j = 0; j < _paChannels; ++j) {
764     if (paVolume < i->volume.values[j]) {
765       paVolume = i->volume.values[j];
766     }
767   }
768   _paVolume = paVolume;  // Get the max volume for any channel
769   _paMute = i->mute;     // Get mute status
770 }
771 
PaSourceInfoCallbackHandler(const pa_source_info * i,int eol)772 void AudioMixerManagerLinuxPulse::PaSourceInfoCallbackHandler(
773     const pa_source_info* i,
774     int eol) {
775   if (eol) {
776     // Signal that we are done
777     LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
778     return;
779   }
780 
781   _paChannels = i->channel_map.channels;   // Get number of channels
782   pa_volume_t paVolume = PA_VOLUME_MUTED;  // Minimum possible value.
783   for (int j = 0; j < _paChannels; ++j) {
784     if (paVolume < i->volume.values[j]) {
785       paVolume = i->volume.values[j];
786     }
787   }
788   _paVolume = paVolume;  // Get the max volume for any channel
789   _paMute = i->mute;     // Get mute status
790 
791   // supported since PA 0.9.15
792   //_paVolSteps = i->n_volume_steps; // Get the number of volume steps
793   // default value is PA_VOLUME_NORM+1
794   _paVolSteps = PA_VOLUME_NORM + 1;
795 }
796 
WaitForOperationCompletion(pa_operation * paOperation) const797 void AudioMixerManagerLinuxPulse::WaitForOperationCompletion(
798     pa_operation* paOperation) const {
799   while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) {
800     LATE(pa_threaded_mainloop_wait)(_paMainloop);
801   }
802 
803   LATE(pa_operation_unref)(paOperation);
804 }
805 
GetSinkInputInfo() const806 bool AudioMixerManagerLinuxPulse::GetSinkInputInfo() const {
807   pa_operation* paOperation = NULL;
808 
809   AutoPulseLock auto_lock(_paMainloop);
810   // Get info for this stream (sink input).
811   paOperation = LATE(pa_context_get_sink_input_info)(
812       _paContext, LATE(pa_stream_get_index)(_paPlayStream),
813       PaSinkInputInfoCallback, (void*)this);
814 
815   WaitForOperationCompletion(paOperation);
816   return true;
817 }
818 
GetSinkInfoByIndex(int device_index) const819 bool AudioMixerManagerLinuxPulse::GetSinkInfoByIndex(int device_index) const {
820   pa_operation* paOperation = NULL;
821 
822   AutoPulseLock auto_lock(_paMainloop);
823   paOperation = LATE(pa_context_get_sink_info_by_index)(
824       _paContext, device_index, PaSinkInfoCallback, (void*)this);
825 
826   WaitForOperationCompletion(paOperation);
827   return true;
828 }
829 
GetSourceInfoByIndex(int device_index) const830 bool AudioMixerManagerLinuxPulse::GetSourceInfoByIndex(int device_index) const {
831   pa_operation* paOperation = NULL;
832 
833   AutoPulseLock auto_lock(_paMainloop);
834   paOperation = LATE(pa_context_get_source_info_by_index)(
835       _paContext, device_index, PaSourceInfoCallback, (void*)this);
836 
837   WaitForOperationCompletion(paOperation);
838   return true;
839 }
840 
841 }  // namespace webrtc
842