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