tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

audio_mixer_manager_mac.cc (28486B)


      1 /*
      2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "modules/audio_device/mac/audio_mixer_manager_mac.h"
     12 
     13 #include <unistd.h>  // getpid()
     14 
     15 #include "rtc_base/system/arch.h"
     16 
     17 namespace webrtc {
     18 
     19 #define WEBRTC_CA_RETURN_ON_ERR(expr)                                     \
     20  do {                                                                    \
     21    err = expr;                                                           \
     22    if (err != noErr) {                                                   \
     23      logCAMsg(::webrtc::LS_ERROR, "Error in " #expr, (const char*)&err); \
     24      return -1;                                                          \
     25    }                                                                     \
     26  } while (0)
     27 
     28 #define WEBRTC_CA_LOG_ERR(expr)                                           \
     29  do {                                                                    \
     30    err = expr;                                                           \
     31    if (err != noErr) {                                                   \
     32      logCAMsg(::webrtc::LS_ERROR, "Error in " #expr, (const char*)&err); \
     33    }                                                                     \
     34  } while (0)
     35 
     36 #define WEBRTC_CA_LOG_WARN(expr)                                            \
     37  do {                                                                      \
     38    err = expr;                                                             \
     39    if (err != noErr) {                                                     \
     40      logCAMsg(::webrtc::LS_WARNING, "Error in " #expr, (const char*)&err); \
     41    }                                                                       \
     42  } while (0)
     43 
     44 AudioMixerManagerMac::AudioMixerManagerMac()
     45    : _inputDeviceID(kAudioObjectUnknown),
     46      _outputDeviceID(kAudioObjectUnknown),
     47      _noInputChannels(0),
     48      _noOutputChannels(0) {
     49  RTC_DLOG(LS_INFO) << __FUNCTION__ << " created";
     50 }
     51 
     52 AudioMixerManagerMac::~AudioMixerManagerMac() {
     53  RTC_DLOG(LS_INFO) << __FUNCTION__ << " destroyed";
     54  Close();
     55 }
     56 
     57 // ============================================================================
     58 //	                                PUBLIC METHODS
     59 // ============================================================================
     60 
     61 int32_t AudioMixerManagerMac::Close() {
     62  RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
     63 
     64  MutexLock lock(&mutex_);
     65 
     66  CloseSpeakerLocked();
     67  CloseMicrophoneLocked();
     68 
     69  return 0;
     70 }
     71 
     72 int32_t AudioMixerManagerMac::CloseSpeaker() {
     73  MutexLock lock(&mutex_);
     74  return CloseSpeakerLocked();
     75 }
     76 
     77 int32_t AudioMixerManagerMac::CloseSpeakerLocked() {
     78  RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
     79 
     80  _outputDeviceID = kAudioObjectUnknown;
     81  _noOutputChannels = 0;
     82 
     83  return 0;
     84 }
     85 
     86 int32_t AudioMixerManagerMac::CloseMicrophone() {
     87  MutexLock lock(&mutex_);
     88  return CloseMicrophoneLocked();
     89 }
     90 
     91 int32_t AudioMixerManagerMac::CloseMicrophoneLocked() {
     92  RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
     93 
     94  _inputDeviceID = kAudioObjectUnknown;
     95  _noInputChannels = 0;
     96 
     97  return 0;
     98 }
     99 
    100 int32_t AudioMixerManagerMac::OpenSpeaker(AudioDeviceID deviceID) {
    101  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::OpenSpeaker(id=" << deviceID
    102                      << ")";
    103 
    104  MutexLock lock(&mutex_);
    105 
    106  OSStatus err = noErr;
    107  UInt32 size = 0;
    108  pid_t hogPid = -1;
    109 
    110  _outputDeviceID = deviceID;
    111 
    112  // Check which process, if any, has hogged the device.
    113  AudioObjectPropertyAddress propertyAddress = {
    114      kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeOutput, 0};
    115 
    116  // First, does it have the property? Aggregate devices don't.
    117  if (AudioObjectHasProperty(_outputDeviceID, &propertyAddress)) {
    118    size = sizeof(hogPid);
    119    WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    120        _outputDeviceID, &propertyAddress, 0, NULL, &size, &hogPid));
    121 
    122    if (hogPid == -1) {
    123      RTC_LOG(LS_VERBOSE) << "No process has hogged the output device";
    124    }
    125    // getpid() is apparently "always successful"
    126    else if (hogPid == getpid()) {
    127      RTC_LOG(LS_VERBOSE) << "Our process has hogged the output device";
    128    } else {
    129      RTC_LOG(LS_WARNING) << "Another process (pid = "
    130                          << static_cast<int>(hogPid)
    131                          << ") has hogged the output device";
    132 
    133      return -1;
    134    }
    135  }
    136 
    137  // get number of channels from stream format
    138  propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
    139 
    140  // Get the stream format, to be able to read the number of channels.
    141  AudioStreamBasicDescription streamFormat;
    142  size = sizeof(AudioStreamBasicDescription);
    143  memset(&streamFormat, 0, size);
    144  WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    145      _outputDeviceID, &propertyAddress, 0, NULL, &size, &streamFormat));
    146 
    147  _noOutputChannels = streamFormat.mChannelsPerFrame;
    148 
    149  return 0;
    150 }
    151 
    152 int32_t AudioMixerManagerMac::OpenMicrophone(AudioDeviceID deviceID) {
    153  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::OpenMicrophone(id=" << deviceID
    154                      << ")";
    155 
    156  MutexLock lock(&mutex_);
    157 
    158  OSStatus err = noErr;
    159  UInt32 size = 0;
    160  pid_t hogPid = -1;
    161 
    162  _inputDeviceID = deviceID;
    163 
    164  // Check which process, if any, has hogged the device.
    165  AudioObjectPropertyAddress propertyAddress = {
    166      kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeInput, 0};
    167  size = sizeof(hogPid);
    168  WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    169      _inputDeviceID, &propertyAddress, 0, NULL, &size, &hogPid));
    170  if (hogPid == -1) {
    171    RTC_LOG(LS_VERBOSE) << "No process has hogged the input device";
    172  }
    173  // getpid() is apparently "always successful"
    174  else if (hogPid == getpid()) {
    175    RTC_LOG(LS_VERBOSE) << "Our process has hogged the input device";
    176  } else {
    177    RTC_LOG(LS_WARNING) << "Another process (pid = " << static_cast<int>(hogPid)
    178                        << ") has hogged the input device";
    179 
    180    return -1;
    181  }
    182 
    183  // get number of channels from stream format
    184  propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
    185 
    186  // Get the stream format, to be able to read the number of channels.
    187  AudioStreamBasicDescription streamFormat;
    188  size = sizeof(AudioStreamBasicDescription);
    189  memset(&streamFormat, 0, size);
    190  WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    191      _inputDeviceID, &propertyAddress, 0, NULL, &size, &streamFormat));
    192 
    193  _noInputChannels = streamFormat.mChannelsPerFrame;
    194 
    195  return 0;
    196 }
    197 
    198 bool AudioMixerManagerMac::SpeakerIsInitialized() const {
    199  RTC_DLOG(LS_INFO) << __FUNCTION__;
    200 
    201  return (_outputDeviceID != kAudioObjectUnknown);
    202 }
    203 
    204 bool AudioMixerManagerMac::MicrophoneIsInitialized() const {
    205  RTC_DLOG(LS_INFO) << __FUNCTION__;
    206 
    207  return (_inputDeviceID != kAudioObjectUnknown);
    208 }
    209 
    210 int32_t AudioMixerManagerMac::SetSpeakerVolume(uint32_t volume) {
    211  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SetSpeakerVolume(volume="
    212                      << volume << ")";
    213 
    214  MutexLock lock(&mutex_);
    215 
    216  if (_outputDeviceID == kAudioObjectUnknown) {
    217    RTC_LOG(LS_WARNING) << "device ID has not been set";
    218    return -1;
    219  }
    220 
    221  OSStatus err = noErr;
    222  UInt32 size = 0;
    223  bool success = false;
    224 
    225  // volume range is 0.0 - 1.0, convert from 0 -255
    226  const Float32 vol = (Float32)(volume / 255.0);
    227 
    228  RTC_DCHECK(vol <= 1.0 && vol >= 0.0);
    229 
    230  // Does the capture device have a master volume control?
    231  // If so, use it exclusively.
    232  AudioObjectPropertyAddress propertyAddress = {
    233      kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 0};
    234  Boolean isSettable = false;
    235  err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
    236                                      &isSettable);
    237  if (err == noErr && isSettable) {
    238    size = sizeof(vol);
    239    WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
    240        _outputDeviceID, &propertyAddress, 0, NULL, size, &vol));
    241 
    242    return 0;
    243  }
    244 
    245  // Otherwise try to set each channel.
    246  for (UInt32 i = 1; i <= _noOutputChannels; i++) {
    247    propertyAddress.mElement = i;
    248    isSettable = false;
    249    err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
    250                                        &isSettable);
    251    if (err == noErr && isSettable) {
    252      size = sizeof(vol);
    253      WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
    254          _outputDeviceID, &propertyAddress, 0, NULL, size, &vol));
    255    }
    256    success = true;
    257  }
    258 
    259  if (!success) {
    260    RTC_LOG(LS_WARNING) << "Unable to set a volume on any output channel";
    261    return -1;
    262  }
    263 
    264  return 0;
    265 }
    266 
    267 int32_t AudioMixerManagerMac::SpeakerVolume(uint32_t& volume) const {
    268  if (_outputDeviceID == kAudioObjectUnknown) {
    269    RTC_LOG(LS_WARNING) << "device ID has not been set";
    270    return -1;
    271  }
    272 
    273  OSStatus err = noErr;
    274  UInt32 size = 0;
    275  unsigned int channels = 0;
    276  Float32 channelVol = 0;
    277  Float32 vol = 0;
    278 
    279  // Does the device have a master volume control?
    280  // If so, use it exclusively.
    281  AudioObjectPropertyAddress propertyAddress = {
    282      kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 0};
    283  Boolean hasProperty =
    284      AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
    285  if (hasProperty) {
    286    size = sizeof(vol);
    287    WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    288        _outputDeviceID, &propertyAddress, 0, NULL, &size, &vol));
    289 
    290    // vol 0.0 to 1.0 -> convert to 0 - 255
    291    volume = static_cast<uint32_t>(vol * 255 + 0.5);
    292  } else {
    293    // Otherwise get the average volume across channels.
    294    vol = 0;
    295    for (UInt32 i = 1; i <= _noOutputChannels; i++) {
    296      channelVol = 0;
    297      propertyAddress.mElement = i;
    298      hasProperty = AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
    299      if (hasProperty) {
    300        size = sizeof(channelVol);
    301        WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    302            _outputDeviceID, &propertyAddress, 0, NULL, &size, &channelVol));
    303 
    304        vol += channelVol;
    305        channels++;
    306      }
    307    }
    308 
    309    if (channels == 0) {
    310      RTC_LOG(LS_WARNING) << "Unable to get a volume on any channel";
    311      return -1;
    312    }
    313 
    314    RTC_DCHECK_GT(channels, 0);
    315    // vol 0.0 to 1.0 -> convert to 0 - 255
    316    volume = static_cast<uint32_t>(255 * vol / channels + 0.5);
    317  }
    318 
    319  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SpeakerVolume() => vol=" << vol;
    320 
    321  return 0;
    322 }
    323 
    324 int32_t AudioMixerManagerMac::MaxSpeakerVolume(uint32_t& maxVolume) const {
    325  if (_outputDeviceID == kAudioObjectUnknown) {
    326    RTC_LOG(LS_WARNING) << "device ID has not been set";
    327    return -1;
    328  }
    329 
    330  // volume range is 0.0 to 1.0
    331  // we convert that to 0 - 255
    332  maxVolume = 255;
    333 
    334  return 0;
    335 }
    336 
    337 int32_t AudioMixerManagerMac::MinSpeakerVolume(uint32_t& minVolume) const {
    338  if (_outputDeviceID == kAudioObjectUnknown) {
    339    RTC_LOG(LS_WARNING) << "device ID has not been set";
    340    return -1;
    341  }
    342 
    343  // volume range is 0.0 to 1.0
    344  // we convert that to 0 - 255
    345  minVolume = 0;
    346 
    347  return 0;
    348 }
    349 
    350 int32_t AudioMixerManagerMac::SpeakerVolumeIsAvailable(bool& available) {
    351  if (_outputDeviceID == kAudioObjectUnknown) {
    352    RTC_LOG(LS_WARNING) << "device ID has not been set";
    353    return -1;
    354  }
    355 
    356  OSStatus err = noErr;
    357 
    358  // Does the capture device have a master volume control?
    359  // If so, use it exclusively.
    360  AudioObjectPropertyAddress propertyAddress = {
    361      kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 0};
    362  Boolean isSettable = false;
    363  err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
    364                                      &isSettable);
    365  if (err == noErr && isSettable) {
    366    available = true;
    367    return 0;
    368  }
    369 
    370  // Otherwise try to set each channel.
    371  for (UInt32 i = 1; i <= _noOutputChannels; i++) {
    372    propertyAddress.mElement = i;
    373    isSettable = false;
    374    err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
    375                                        &isSettable);
    376    if (err != noErr || !isSettable) {
    377      available = false;
    378      RTC_LOG(LS_WARNING) << "Volume cannot be set for output channel " << i
    379                          << ", err=" << err;
    380      return -1;
    381    }
    382  }
    383 
    384  available = true;
    385  return 0;
    386 }
    387 
    388 int32_t AudioMixerManagerMac::SpeakerMuteIsAvailable(bool& available) {
    389  if (_outputDeviceID == kAudioObjectUnknown) {
    390    RTC_LOG(LS_WARNING) << "device ID has not been set";
    391    return -1;
    392  }
    393 
    394  OSStatus err = noErr;
    395 
    396  // Does the capture device have a master mute control?
    397  // If so, use it exclusively.
    398  AudioObjectPropertyAddress propertyAddress = {
    399      kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, 0};
    400  Boolean isSettable = false;
    401  err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
    402                                      &isSettable);
    403  if (err == noErr && isSettable) {
    404    available = true;
    405    return 0;
    406  }
    407 
    408  // Otherwise try to set each channel.
    409  for (UInt32 i = 1; i <= _noOutputChannels; i++) {
    410    propertyAddress.mElement = i;
    411    isSettable = false;
    412    err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
    413                                        &isSettable);
    414    if (err != noErr || !isSettable) {
    415      available = false;
    416      RTC_LOG(LS_WARNING) << "Mute cannot be set for output channel " << i
    417                          << ", err=" << err;
    418      return -1;
    419    }
    420  }
    421 
    422  available = true;
    423  return 0;
    424 }
    425 
    426 int32_t AudioMixerManagerMac::SetSpeakerMute(bool enable) {
    427  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SetSpeakerMute(enable="
    428                      << enable << ")";
    429 
    430  MutexLock lock(&mutex_);
    431 
    432  if (_outputDeviceID == kAudioObjectUnknown) {
    433    RTC_LOG(LS_WARNING) << "device ID has not been set";
    434    return -1;
    435  }
    436 
    437  OSStatus err = noErr;
    438  UInt32 size = 0;
    439  UInt32 mute = enable ? 1 : 0;
    440  bool success = false;
    441 
    442  // Does the render device have a master mute control?
    443  // If so, use it exclusively.
    444  AudioObjectPropertyAddress propertyAddress = {
    445      kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, 0};
    446  Boolean isSettable = false;
    447  err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
    448                                      &isSettable);
    449  if (err == noErr && isSettable) {
    450    size = sizeof(mute);
    451    WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
    452        _outputDeviceID, &propertyAddress, 0, NULL, size, &mute));
    453 
    454    return 0;
    455  }
    456 
    457  // Otherwise try to set each channel.
    458  for (UInt32 i = 1; i <= _noOutputChannels; i++) {
    459    propertyAddress.mElement = i;
    460    isSettable = false;
    461    err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
    462                                        &isSettable);
    463    if (err == noErr && isSettable) {
    464      size = sizeof(mute);
    465      WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
    466          _outputDeviceID, &propertyAddress, 0, NULL, size, &mute));
    467    }
    468    success = true;
    469  }
    470 
    471  if (!success) {
    472    RTC_LOG(LS_WARNING) << "Unable to set mute on any input channel";
    473    return -1;
    474  }
    475 
    476  return 0;
    477 }
    478 
    479 int32_t AudioMixerManagerMac::SpeakerMute(bool& enabled) const {
    480  if (_outputDeviceID == kAudioObjectUnknown) {
    481    RTC_LOG(LS_WARNING) << "device ID has not been set";
    482    return -1;
    483  }
    484 
    485  OSStatus err = noErr;
    486  UInt32 size = 0;
    487  unsigned int channels = 0;
    488  UInt32 channelMuted = 0;
    489  UInt32 muted = 0;
    490 
    491  // Does the device have a master volume control?
    492  // If so, use it exclusively.
    493  AudioObjectPropertyAddress propertyAddress = {
    494      kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, 0};
    495  Boolean hasProperty =
    496      AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
    497  if (hasProperty) {
    498    size = sizeof(muted);
    499    WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    500        _outputDeviceID, &propertyAddress, 0, NULL, &size, &muted));
    501 
    502    // 1 means muted
    503    enabled = static_cast<bool>(muted);
    504  } else {
    505    // Otherwise check if all channels are muted.
    506    for (UInt32 i = 1; i <= _noOutputChannels; i++) {
    507      muted = 0;
    508      propertyAddress.mElement = i;
    509      hasProperty = AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
    510      if (hasProperty) {
    511        size = sizeof(channelMuted);
    512        WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    513            _outputDeviceID, &propertyAddress, 0, NULL, &size, &channelMuted));
    514 
    515        muted = (muted && channelMuted);
    516        channels++;
    517      }
    518    }
    519 
    520    if (channels == 0) {
    521      RTC_LOG(LS_WARNING) << "Unable to get mute for any channel";
    522      return -1;
    523    }
    524 
    525    RTC_DCHECK_GT(channels, 0);
    526    // 1 means muted
    527    enabled = static_cast<bool>(muted);
    528  }
    529 
    530  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SpeakerMute() => enabled="
    531                      << enabled;
    532 
    533  return 0;
    534 }
    535 
    536 int32_t AudioMixerManagerMac::StereoPlayoutIsAvailable(bool& available) {
    537  if (_outputDeviceID == kAudioObjectUnknown) {
    538    RTC_LOG(LS_WARNING) << "device ID has not been set";
    539    return -1;
    540  }
    541 
    542  available = (_noOutputChannels == 2);
    543  return 0;
    544 }
    545 
    546 int32_t AudioMixerManagerMac::StereoRecordingIsAvailable(bool& available) {
    547  if (_inputDeviceID == kAudioObjectUnknown) {
    548    RTC_LOG(LS_WARNING) << "device ID has not been set";
    549    return -1;
    550  }
    551 
    552  available = (_noInputChannels == 2);
    553  return 0;
    554 }
    555 
    556 int32_t AudioMixerManagerMac::MicrophoneMuteIsAvailable(bool& available) {
    557  if (_inputDeviceID == kAudioObjectUnknown) {
    558    RTC_LOG(LS_WARNING) << "device ID has not been set";
    559    return -1;
    560  }
    561 
    562  OSStatus err = noErr;
    563 
    564  // Does the capture device have a master mute control?
    565  // If so, use it exclusively.
    566  AudioObjectPropertyAddress propertyAddress = {
    567      kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, 0};
    568  Boolean isSettable = false;
    569  err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
    570                                      &isSettable);
    571  if (err == noErr && isSettable) {
    572    available = true;
    573    return 0;
    574  }
    575 
    576  // Otherwise try to set each channel.
    577  for (UInt32 i = 1; i <= _noInputChannels; i++) {
    578    propertyAddress.mElement = i;
    579    isSettable = false;
    580    err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
    581                                        &isSettable);
    582    if (err != noErr || !isSettable) {
    583      available = false;
    584      RTC_LOG(LS_WARNING) << "Mute cannot be set for output channel " << i
    585                          << ", err=" << err;
    586      return -1;
    587    }
    588  }
    589 
    590  available = true;
    591  return 0;
    592 }
    593 
    594 int32_t AudioMixerManagerMac::SetMicrophoneMute(bool enable) {
    595  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SetMicrophoneMute(enable="
    596                      << enable << ")";
    597 
    598  MutexLock lock(&mutex_);
    599 
    600  if (_inputDeviceID == kAudioObjectUnknown) {
    601    RTC_LOG(LS_WARNING) << "device ID has not been set";
    602    return -1;
    603  }
    604 
    605  OSStatus err = noErr;
    606  UInt32 size = 0;
    607  UInt32 mute = enable ? 1 : 0;
    608  bool success = false;
    609 
    610  // Does the capture device have a master mute control?
    611  // If so, use it exclusively.
    612  AudioObjectPropertyAddress propertyAddress = {
    613      kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, 0};
    614  Boolean isSettable = false;
    615  err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
    616                                      &isSettable);
    617  if (err == noErr && isSettable) {
    618    size = sizeof(mute);
    619    WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
    620        _inputDeviceID, &propertyAddress, 0, NULL, size, &mute));
    621 
    622    return 0;
    623  }
    624 
    625  // Otherwise try to set each channel.
    626  for (UInt32 i = 1; i <= _noInputChannels; i++) {
    627    propertyAddress.mElement = i;
    628    isSettable = false;
    629    err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
    630                                        &isSettable);
    631    if (err == noErr && isSettable) {
    632      size = sizeof(mute);
    633      WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
    634          _inputDeviceID, &propertyAddress, 0, NULL, size, &mute));
    635    }
    636    success = true;
    637  }
    638 
    639  if (!success) {
    640    RTC_LOG(LS_WARNING) << "Unable to set mute on any input channel";
    641    return -1;
    642  }
    643 
    644  return 0;
    645 }
    646 
    647 int32_t AudioMixerManagerMac::MicrophoneMute(bool& enabled) const {
    648  if (_inputDeviceID == kAudioObjectUnknown) {
    649    RTC_LOG(LS_WARNING) << "device ID has not been set";
    650    return -1;
    651  }
    652 
    653  OSStatus err = noErr;
    654  UInt32 size = 0;
    655  unsigned int channels = 0;
    656  UInt32 channelMuted = 0;
    657  UInt32 muted = 0;
    658 
    659  // Does the device have a master volume control?
    660  // If so, use it exclusively.
    661  AudioObjectPropertyAddress propertyAddress = {
    662      kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, 0};
    663  Boolean hasProperty =
    664      AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
    665  if (hasProperty) {
    666    size = sizeof(muted);
    667    WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    668        _inputDeviceID, &propertyAddress, 0, NULL, &size, &muted));
    669 
    670    // 1 means muted
    671    enabled = static_cast<bool>(muted);
    672  } else {
    673    // Otherwise check if all channels are muted.
    674    for (UInt32 i = 1; i <= _noInputChannels; i++) {
    675      muted = 0;
    676      propertyAddress.mElement = i;
    677      hasProperty = AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
    678      if (hasProperty) {
    679        size = sizeof(channelMuted);
    680        WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    681            _inputDeviceID, &propertyAddress, 0, NULL, &size, &channelMuted));
    682 
    683        muted = (muted && channelMuted);
    684        channels++;
    685      }
    686    }
    687 
    688    if (channels == 0) {
    689      RTC_LOG(LS_WARNING) << "Unable to get mute for any channel";
    690      return -1;
    691    }
    692 
    693    RTC_DCHECK_GT(channels, 0);
    694    // 1 means muted
    695    enabled = static_cast<bool>(muted);
    696  }
    697 
    698  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::MicrophoneMute() => enabled="
    699                      << enabled;
    700 
    701  return 0;
    702 }
    703 
    704 int32_t AudioMixerManagerMac::MicrophoneVolumeIsAvailable(bool& available) {
    705  if (_inputDeviceID == kAudioObjectUnknown) {
    706    RTC_LOG(LS_WARNING) << "device ID has not been set";
    707    return -1;
    708  }
    709 
    710  OSStatus err = noErr;
    711 
    712  // Does the capture device have a master volume control?
    713  // If so, use it exclusively.
    714  AudioObjectPropertyAddress propertyAddress = {
    715      kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, 0};
    716  Boolean isSettable = false;
    717  err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
    718                                      &isSettable);
    719  if (err == noErr && isSettable) {
    720    available = true;
    721    return 0;
    722  }
    723 
    724  // Otherwise try to set each channel.
    725  for (UInt32 i = 1; i <= _noInputChannels; i++) {
    726    propertyAddress.mElement = i;
    727    isSettable = false;
    728    err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
    729                                        &isSettable);
    730    if (err != noErr || !isSettable) {
    731      available = false;
    732      RTC_LOG(LS_WARNING) << "Volume cannot be set for input channel " << i
    733                          << ", err=" << err;
    734      return -1;
    735    }
    736  }
    737 
    738  available = true;
    739  return 0;
    740 }
    741 
    742 int32_t AudioMixerManagerMac::SetMicrophoneVolume(uint32_t volume) {
    743  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SetMicrophoneVolume(volume="
    744                      << volume << ")";
    745 
    746  MutexLock lock(&mutex_);
    747 
    748  if (_inputDeviceID == kAudioObjectUnknown) {
    749    RTC_LOG(LS_WARNING) << "device ID has not been set";
    750    return -1;
    751  }
    752 
    753  OSStatus err = noErr;
    754  UInt32 size = 0;
    755  bool success = false;
    756 
    757  // volume range is 0.0 - 1.0, convert from 0 - 255
    758  const Float32 vol = (Float32)(volume / 255.0);
    759 
    760  RTC_DCHECK(vol <= 1.0 && vol >= 0.0);
    761 
    762  // Does the capture device have a master volume control?
    763  // If so, use it exclusively.
    764  AudioObjectPropertyAddress propertyAddress = {
    765      kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, 0};
    766  Boolean isSettable = false;
    767  err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
    768                                      &isSettable);
    769  if (err == noErr && isSettable) {
    770    size = sizeof(vol);
    771    WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
    772        _inputDeviceID, &propertyAddress, 0, NULL, size, &vol));
    773 
    774    return 0;
    775  }
    776 
    777  // Otherwise try to set each channel.
    778  for (UInt32 i = 1; i <= _noInputChannels; i++) {
    779    propertyAddress.mElement = i;
    780    isSettable = false;
    781    err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
    782                                        &isSettable);
    783    if (err == noErr && isSettable) {
    784      size = sizeof(vol);
    785      WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
    786          _inputDeviceID, &propertyAddress, 0, NULL, size, &vol));
    787    }
    788    success = true;
    789  }
    790 
    791  if (!success) {
    792    RTC_LOG(LS_WARNING) << "Unable to set a level on any input channel";
    793    return -1;
    794  }
    795 
    796  return 0;
    797 }
    798 
    799 int32_t AudioMixerManagerMac::MicrophoneVolume(uint32_t& volume) const {
    800  if (_inputDeviceID == kAudioObjectUnknown) {
    801    RTC_LOG(LS_WARNING) << "device ID has not been set";
    802    return -1;
    803  }
    804 
    805  OSStatus err = noErr;
    806  UInt32 size = 0;
    807  unsigned int channels = 0;
    808  Float32 channelVol = 0;
    809  Float32 volFloat32 = 0;
    810 
    811  // Does the device have a master volume control?
    812  // If so, use it exclusively.
    813  AudioObjectPropertyAddress propertyAddress = {
    814      kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, 0};
    815  Boolean hasProperty =
    816      AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
    817  if (hasProperty) {
    818    size = sizeof(volFloat32);
    819    WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    820        _inputDeviceID, &propertyAddress, 0, NULL, &size, &volFloat32));
    821 
    822    // vol 0.0 to 1.0 -> convert to 0 - 255
    823    volume = static_cast<uint32_t>(volFloat32 * 255 + 0.5);
    824  } else {
    825    // Otherwise get the average volume across channels.
    826    volFloat32 = 0;
    827    for (UInt32 i = 1; i <= _noInputChannels; i++) {
    828      channelVol = 0;
    829      propertyAddress.mElement = i;
    830      hasProperty = AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
    831      if (hasProperty) {
    832        size = sizeof(channelVol);
    833        WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
    834            _inputDeviceID, &propertyAddress, 0, NULL, &size, &channelVol));
    835 
    836        volFloat32 += channelVol;
    837        channels++;
    838      }
    839    }
    840 
    841    if (channels == 0) {
    842      RTC_LOG(LS_WARNING) << "Unable to get a level on any channel";
    843      return -1;
    844    }
    845 
    846    RTC_DCHECK_GT(channels, 0);
    847    // vol 0.0 to 1.0 -> convert to 0 - 255
    848    volume = static_cast<uint32_t>(255 * volFloat32 / channels + 0.5);
    849  }
    850 
    851  RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::MicrophoneVolume() => vol="
    852                      << volume;
    853 
    854  return 0;
    855 }
    856 
    857 int32_t AudioMixerManagerMac::MaxMicrophoneVolume(uint32_t& maxVolume) const {
    858  if (_inputDeviceID == kAudioObjectUnknown) {
    859    RTC_LOG(LS_WARNING) << "device ID has not been set";
    860    return -1;
    861  }
    862 
    863  // volume range is 0.0 to 1.0
    864  // we convert that to 0 - 255
    865  maxVolume = 255;
    866 
    867  return 0;
    868 }
    869 
    870 int32_t AudioMixerManagerMac::MinMicrophoneVolume(uint32_t& minVolume) const {
    871  if (_inputDeviceID == kAudioObjectUnknown) {
    872    RTC_LOG(LS_WARNING) << "device ID has not been set";
    873    return -1;
    874  }
    875 
    876  // volume range is 0.0 to 1.0
    877  // we convert that to 0 - 10
    878  minVolume = 0;
    879 
    880  return 0;
    881 }
    882 
    883 // ============================================================================
    884 //                                 Private Methods
    885 // ============================================================================
    886 
    887 // CoreAudio errors are best interpreted as four character strings.
    888 void AudioMixerManagerMac::logCAMsg(const LoggingSeverity sev,
    889                                    const char* msg,
    890                                    const char* err) {
    891  RTC_DCHECK(msg != NULL);
    892  RTC_DCHECK(err != NULL);
    893  RTC_DCHECK(sev == LS_ERROR || sev == LS_WARNING);
    894 
    895 #ifdef WEBRTC_ARCH_BIG_ENDIAN
    896  switch (sev) {
    897    case LS_ERROR:
    898      RTC_LOG(LS_ERROR) << msg << ": " << err[0] << err[1] << err[2] << err[3];
    899      break;
    900    case LS_WARNING:
    901      RTC_LOG(LS_WARNING) << msg << ": " << err[0] << err[1] << err[2]
    902                          << err[3];
    903      break;
    904    default:
    905      break;
    906  }
    907 #else
    908  // We need to flip the characters in this case.
    909  switch (sev) {
    910    case LS_ERROR:
    911      RTC_LOG(LS_ERROR) << msg << ": " << err[3] << err[2] << err[1] << err[0];
    912      break;
    913    case LS_WARNING:
    914      RTC_LOG(LS_WARNING) << msg << ": " << err[3] << err[2] << err[1]
    915                          << err[0];
    916      break;
    917    default:
    918      break;
    919  }
    920 #endif
    921 }
    922 
    923 }  // namespace webrtc
    924 // EOF