Home | History | Annotate | Download | only in linux
      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:
     31   explicit AutoPulseLock(pa_threaded_mainloop* pa_mainloop)
     32       : pa_mainloop_(pa_mainloop) {
     33     LATE(pa_threaded_mainloop_lock)(pa_mainloop_);
     34   }
     35 
     36   ~AutoPulseLock() {
     37     LATE(pa_threaded_mainloop_unlock)(pa_mainloop_);
     38   }
     39 
     40  private:
     41   pa_threaded_mainloop* const pa_mainloop_;
     42 };
     43 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    219 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 
    228 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 
    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
    307 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
    339 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
    357 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
    373 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
    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
    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 
    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 
    482 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
    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
    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 
    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 
    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 
    662 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
    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 
    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 
    746 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 
    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
    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
    858 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
    897 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
    916 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 
    931 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
    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
    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
   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
   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 
   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 
   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 
   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 
   1104 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 
   1115 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 
   1130 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 
   1142 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