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