• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3   *
4   *  Use of this source code is governed by a BSD-style license
5   *  that can be found in the LICENSE file in the root of the source
6   *  tree. An additional intellectual property rights grant can be found
7   *  in the file PATENTS.  All contributing project authors may
8   *  be found in the AUTHORS file in the root of the source tree.
9   */
10  
11  #include <assert.h>
12  
13  #include "webrtc/modules/audio_device/linux/audio_mixer_manager_pulse_linux.h"
14  #include "webrtc/system_wrappers/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