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