Home | History | Annotate | Download | only in mac
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "media/audio/mac/audio_low_latency_input_mac.h"
      6 
      7 #include <CoreServices/CoreServices.h>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/logging.h"
     11 #include "base/mac/mac_logging.h"
     12 #include "media/audio/mac/audio_manager_mac.h"
     13 #include "media/base/audio_bus.h"
     14 #include "media/base/data_buffer.h"
     15 
     16 namespace media {
     17 
     18 // Number of blocks of buffers used in the |fifo_|.
     19 const int kNumberOfBlocksBufferInFifo = 2;
     20 
     21 static std::ostream& operator<<(std::ostream& os,
     22                                 const AudioStreamBasicDescription& format) {
     23   os << "sample rate       : " << format.mSampleRate << std::endl
     24      << "format ID         : " << format.mFormatID << std::endl
     25      << "format flags      : " << format.mFormatFlags << std::endl
     26      << "bytes per packet  : " << format.mBytesPerPacket << std::endl
     27      << "frames per packet : " << format.mFramesPerPacket << std::endl
     28      << "bytes per frame   : " << format.mBytesPerFrame << std::endl
     29      << "channels per frame: " << format.mChannelsPerFrame << std::endl
     30      << "bits per channel  : " << format.mBitsPerChannel;
     31   return os;
     32 }
     33 
     34 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit"
     35 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
     36 // for more details and background regarding this implementation.
     37 
     38 AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager,
     39                                        const AudioParameters& input_params,
     40                                        AudioDeviceID audio_device_id)
     41     : manager_(manager),
     42       number_of_frames_(input_params.frames_per_buffer()),
     43       sink_(NULL),
     44       audio_unit_(0),
     45       input_device_id_(audio_device_id),
     46       started_(false),
     47       hardware_latency_frames_(0),
     48       number_of_channels_in_frame_(0),
     49       fifo_(input_params.channels(),
     50             number_of_frames_,
     51             kNumberOfBlocksBufferInFifo) {
     52   DCHECK(manager_);
     53 
     54   // Set up the desired (output) format specified by the client.
     55   format_.mSampleRate = input_params.sample_rate();
     56   format_.mFormatID = kAudioFormatLinearPCM;
     57   format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
     58                          kLinearPCMFormatFlagIsSignedInteger;
     59   format_.mBitsPerChannel = input_params.bits_per_sample();
     60   format_.mChannelsPerFrame = input_params.channels();
     61   format_.mFramesPerPacket = 1;  // uncompressed audio
     62   format_.mBytesPerPacket = (format_.mBitsPerChannel *
     63                              input_params.channels()) / 8;
     64   format_.mBytesPerFrame = format_.mBytesPerPacket;
     65   format_.mReserved = 0;
     66 
     67   DVLOG(1) << "Desired ouput format: " << format_;
     68 
     69   // Derive size (in bytes) of the buffers that we will render to.
     70   UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame;
     71   DVLOG(1) << "Size of data buffer in bytes : " << data_byte_size;
     72 
     73   // Allocate AudioBuffers to be used as storage for the received audio.
     74   // The AudioBufferList structure works as a placeholder for the
     75   // AudioBuffer structure, which holds a pointer to the actual data buffer.
     76   audio_data_buffer_.reset(new uint8[data_byte_size]);
     77   audio_buffer_list_.mNumberBuffers = 1;
     78 
     79   AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
     80   audio_buffer->mNumberChannels = input_params.channels();
     81   audio_buffer->mDataByteSize = data_byte_size;
     82   audio_buffer->mData = audio_data_buffer_.get();
     83 }
     84 
     85 AUAudioInputStream::~AUAudioInputStream() {}
     86 
     87 // Obtain and open the AUHAL AudioOutputUnit for recording.
     88 bool AUAudioInputStream::Open() {
     89   // Verify that we are not already opened.
     90   if (audio_unit_)
     91     return false;
     92 
     93   // Verify that we have a valid device.
     94   if (input_device_id_ == kAudioObjectUnknown) {
     95     NOTREACHED() << "Device ID is unknown";
     96     return false;
     97   }
     98 
     99   // Start by obtaining an AudioOuputUnit using an AUHAL component description.
    100 
    101   // Description for the Audio Unit we want to use (AUHAL in this case).
    102   AudioComponentDescription desc = {
    103       kAudioUnitType_Output,
    104       kAudioUnitSubType_HALOutput,
    105       kAudioUnitManufacturer_Apple,
    106       0,
    107       0
    108   };
    109 
    110   AudioComponent comp = AudioComponentFindNext(0, &desc);
    111   DCHECK(comp);
    112 
    113   // Get access to the service provided by the specified Audio Unit.
    114   OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
    115   if (result) {
    116     HandleError(result);
    117     return false;
    118   }
    119 
    120   // Enable IO on the input scope of the Audio Unit.
    121 
    122   // After creating the AUHAL object, we must enable IO on the input scope
    123   // of the Audio Unit to obtain the device input. Input must be explicitly
    124   // enabled with the kAudioOutputUnitProperty_EnableIO property on Element 1
    125   // of the AUHAL. Beacause the AUHAL can be used for both input and output,
    126   // we must also disable IO on the output scope.
    127 
    128   UInt32 enableIO = 1;
    129 
    130   // Enable input on the AUHAL.
    131   result = AudioUnitSetProperty(audio_unit_,
    132                                 kAudioOutputUnitProperty_EnableIO,
    133                                 kAudioUnitScope_Input,
    134                                 1,          // input element 1
    135                                 &enableIO,  // enable
    136                                 sizeof(enableIO));
    137   if (result) {
    138     HandleError(result);
    139     return false;
    140   }
    141 
    142   // Disable output on the AUHAL.
    143   enableIO = 0;
    144   result = AudioUnitSetProperty(audio_unit_,
    145                                 kAudioOutputUnitProperty_EnableIO,
    146                                 kAudioUnitScope_Output,
    147                                 0,          // output element 0
    148                                 &enableIO,  // disable
    149                                 sizeof(enableIO));
    150   if (result) {
    151     HandleError(result);
    152     return false;
    153   }
    154 
    155   // Next, set the audio device to be the Audio Unit's current device.
    156   // Note that, devices can only be set to the AUHAL after enabling IO.
    157   result = AudioUnitSetProperty(audio_unit_,
    158                                 kAudioOutputUnitProperty_CurrentDevice,
    159                                 kAudioUnitScope_Global,
    160                                 0,
    161                                 &input_device_id_,
    162                                 sizeof(input_device_id_));
    163   if (result) {
    164     HandleError(result);
    165     return false;
    166   }
    167 
    168   // Set up the the desired (output) format.
    169   // For obtaining input from a device, the device format is always expressed
    170   // on the output scope of the AUHAL's Element 1.
    171   result = AudioUnitSetProperty(audio_unit_,
    172                                 kAudioUnitProperty_StreamFormat,
    173                                 kAudioUnitScope_Output,
    174                                 1,
    175                                 &format_,
    176                                 sizeof(format_));
    177   if (result) {
    178     HandleError(result);
    179     return false;
    180   }
    181 
    182   // Set the desired number of frames in the IO buffer (output scope).
    183   // WARNING: Setting this value changes the frame size for all input audio
    184   // units in the current process.  As a result, the AURenderCallback must be
    185   // able to handle arbitrary buffer sizes and FIFO appropriately.
    186   UInt32 buffer_size = 0;
    187   UInt32 property_size = sizeof(buffer_size);
    188   result = AudioUnitGetProperty(audio_unit_,
    189                                 kAudioDevicePropertyBufferFrameSize,
    190                                 kAudioUnitScope_Output,
    191                                 1,
    192                                 &buffer_size,
    193                                 &property_size);
    194   if (result != noErr) {
    195     HandleError(result);
    196     return false;
    197   }
    198 
    199   // Only set the buffer size if we're the only active stream or the buffer size
    200   // is lower than the current buffer size.
    201   if (manager_->input_stream_count() == 1 || number_of_frames_ < buffer_size) {
    202     buffer_size = number_of_frames_;
    203     result = AudioUnitSetProperty(audio_unit_,
    204                                   kAudioDevicePropertyBufferFrameSize,
    205                                   kAudioUnitScope_Output,
    206                                   1,
    207                                   &buffer_size,
    208                                   sizeof(buffer_size));
    209     if (result != noErr) {
    210       HandleError(result);
    211       return false;
    212     }
    213   }
    214 
    215   // Register the input procedure for the AUHAL.
    216   // This procedure will be called when the AUHAL has received new data
    217   // from the input device.
    218   AURenderCallbackStruct callback;
    219   callback.inputProc = InputProc;
    220   callback.inputProcRefCon = this;
    221   result = AudioUnitSetProperty(audio_unit_,
    222                                 kAudioOutputUnitProperty_SetInputCallback,
    223                                 kAudioUnitScope_Global,
    224                                 0,
    225                                 &callback,
    226                                 sizeof(callback));
    227   if (result) {
    228     HandleError(result);
    229     return false;
    230   }
    231 
    232   // Finally, initialize the audio unit and ensure that it is ready to render.
    233   // Allocates memory according to the maximum number of audio frames
    234   // it can produce in response to a single render call.
    235   result = AudioUnitInitialize(audio_unit_);
    236   if (result) {
    237     HandleError(result);
    238     return false;
    239   }
    240 
    241   // The hardware latency is fixed and will not change during the call.
    242   hardware_latency_frames_ = GetHardwareLatency();
    243 
    244   // The master channel is 0, Left and right are channels 1 and 2.
    245   // And the master channel is not counted in |number_of_channels_in_frame_|.
    246   number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();
    247 
    248   return true;
    249 }
    250 
    251 void AUAudioInputStream::Start(AudioInputCallback* callback) {
    252   DCHECK(callback);
    253   DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully";
    254   if (started_ || !audio_unit_)
    255     return;
    256 
    257   // Check if we should defer Start() for http://crbug.com/160920.
    258   if (manager_->ShouldDeferStreamStart()) {
    259     // Use a cancellable closure so that if Stop() is called before Start()
    260     // actually runs, we can cancel the pending start.
    261     deferred_start_cb_.Reset(base::Bind(
    262         &AUAudioInputStream::Start, base::Unretained(this), callback));
    263     manager_->GetTaskRunner()->PostDelayedTask(
    264         FROM_HERE,
    265         deferred_start_cb_.callback(),
    266         base::TimeDelta::FromSeconds(
    267             AudioManagerMac::kStartDelayInSecsForPowerEvents));
    268     return;
    269   }
    270 
    271   sink_ = callback;
    272   StartAgc();
    273   OSStatus result = AudioOutputUnitStart(audio_unit_);
    274   if (result == noErr) {
    275     started_ = true;
    276   }
    277   OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
    278       << "Failed to start acquiring data";
    279 }
    280 
    281 void AUAudioInputStream::Stop() {
    282   if (!started_)
    283     return;
    284   StopAgc();
    285   OSStatus result = AudioOutputUnitStop(audio_unit_);
    286   DCHECK_EQ(result, noErr);
    287   started_ = false;
    288   sink_ = NULL;
    289 
    290   OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
    291       << "Failed to stop acquiring data";
    292 }
    293 
    294 void AUAudioInputStream::Close() {
    295   // It is valid to call Close() before calling open or Start().
    296   // It is also valid to call Close() after Start() has been called.
    297   if (started_) {
    298     Stop();
    299   }
    300   if (audio_unit_) {
    301     // Deallocate the audio units resources.
    302     OSStatus result = AudioUnitUninitialize(audio_unit_);
    303     OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
    304         << "AudioUnitUninitialize() failed.";
    305 
    306     result = AudioComponentInstanceDispose(audio_unit_);
    307     OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
    308         << "AudioComponentInstanceDispose() failed.";
    309 
    310     audio_unit_ = 0;
    311   }
    312 
    313   // Inform the audio manager that we have been closed. This can cause our
    314   // destruction.
    315   manager_->ReleaseInputStream(this);
    316 }
    317 
    318 double AUAudioInputStream::GetMaxVolume() {
    319   // Verify that we have a valid device.
    320   if (input_device_id_ == kAudioObjectUnknown) {
    321     NOTREACHED() << "Device ID is unknown";
    322     return 0.0;
    323   }
    324 
    325   // Query if any of the master, left or right channels has volume control.
    326   for (int i = 0; i <= number_of_channels_in_frame_; ++i) {
    327     // If the volume is settable, the  valid volume range is [0.0, 1.0].
    328     if (IsVolumeSettableOnChannel(i))
    329       return 1.0;
    330   }
    331 
    332   // Volume control is not available for the audio stream.
    333   return 0.0;
    334 }
    335 
    336 void AUAudioInputStream::SetVolume(double volume) {
    337   DVLOG(1) << "SetVolume(volume=" << volume << ")";
    338   DCHECK_GE(volume, 0.0);
    339   DCHECK_LE(volume, 1.0);
    340 
    341   // Verify that we have a valid device.
    342   if (input_device_id_ == kAudioObjectUnknown) {
    343     NOTREACHED() << "Device ID is unknown";
    344     return;
    345   }
    346 
    347   Float32 volume_float32 = static_cast<Float32>(volume);
    348   AudioObjectPropertyAddress property_address = {
    349     kAudioDevicePropertyVolumeScalar,
    350     kAudioDevicePropertyScopeInput,
    351     kAudioObjectPropertyElementMaster
    352   };
    353 
    354   // Try to set the volume for master volume channel.
    355   if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) {
    356     OSStatus result = AudioObjectSetPropertyData(input_device_id_,
    357                                                  &property_address,
    358                                                  0,
    359                                                  NULL,
    360                                                  sizeof(volume_float32),
    361                                                  &volume_float32);
    362     if (result != noErr) {
    363       DLOG(WARNING) << "Failed to set volume to " << volume_float32;
    364     }
    365     return;
    366   }
    367 
    368   // There is no master volume control, try to set volume for each channel.
    369   int successful_channels = 0;
    370   for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
    371     property_address.mElement = static_cast<UInt32>(i);
    372     if (IsVolumeSettableOnChannel(i)) {
    373       OSStatus result = AudioObjectSetPropertyData(input_device_id_,
    374                                                    &property_address,
    375                                                    0,
    376                                                    NULL,
    377                                                    sizeof(volume_float32),
    378                                                    &volume_float32);
    379       if (result == noErr)
    380         ++successful_channels;
    381     }
    382   }
    383 
    384   DLOG_IF(WARNING, successful_channels == 0)
    385       << "Failed to set volume to " << volume_float32;
    386 
    387   // Update the AGC volume level based on the last setting above. Note that,
    388   // the volume-level resolution is not infinite and it is therefore not
    389   // possible to assume that the volume provided as input parameter can be
    390   // used directly. Instead, a new query to the audio hardware is required.
    391   // This method does nothing if AGC is disabled.
    392   UpdateAgcVolume();
    393 }
    394 
    395 double AUAudioInputStream::GetVolume() {
    396   // Verify that we have a valid device.
    397   if (input_device_id_ == kAudioObjectUnknown){
    398     NOTREACHED() << "Device ID is unknown";
    399     return 0.0;
    400   }
    401 
    402   AudioObjectPropertyAddress property_address = {
    403     kAudioDevicePropertyVolumeScalar,
    404     kAudioDevicePropertyScopeInput,
    405     kAudioObjectPropertyElementMaster
    406   };
    407 
    408   if (AudioObjectHasProperty(input_device_id_, &property_address)) {
    409     // The device supports master volume control, get the volume from the
    410     // master channel.
    411     Float32 volume_float32 = 0.0;
    412     UInt32 size = sizeof(volume_float32);
    413     OSStatus result = AudioObjectGetPropertyData(input_device_id_,
    414                                                  &property_address,
    415                                                  0,
    416                                                  NULL,
    417                                                  &size,
    418                                                  &volume_float32);
    419     if (result == noErr)
    420       return static_cast<double>(volume_float32);
    421   } else {
    422     // There is no master volume control, try to get the average volume of
    423     // all the channels.
    424     Float32 volume_float32 = 0.0;
    425     int successful_channels = 0;
    426     for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
    427       property_address.mElement = static_cast<UInt32>(i);
    428       if (AudioObjectHasProperty(input_device_id_, &property_address)) {
    429         Float32 channel_volume = 0;
    430         UInt32 size = sizeof(channel_volume);
    431         OSStatus result = AudioObjectGetPropertyData(input_device_id_,
    432                                                      &property_address,
    433                                                      0,
    434                                                      NULL,
    435                                                      &size,
    436                                                      &channel_volume);
    437         if (result == noErr) {
    438           volume_float32 += channel_volume;
    439           ++successful_channels;
    440         }
    441       }
    442     }
    443 
    444     // Get the average volume of the channels.
    445     if (successful_channels != 0)
    446       return static_cast<double>(volume_float32 / successful_channels);
    447   }
    448 
    449   DLOG(WARNING) << "Failed to get volume";
    450   return 0.0;
    451 }
    452 
    453 bool AUAudioInputStream::IsMuted() {
    454   // Verify that we have a valid device.
    455   DCHECK_NE(input_device_id_, kAudioObjectUnknown) << "Device ID is unknown";
    456 
    457   AudioObjectPropertyAddress property_address = {
    458     kAudioDevicePropertyMute,
    459     kAudioDevicePropertyScopeInput,
    460     kAudioObjectPropertyElementMaster
    461   };
    462 
    463   if (!AudioObjectHasProperty(input_device_id_, &property_address)) {
    464     DLOG(ERROR) << "Device does not support checking master mute state";
    465     return false;
    466   }
    467 
    468   UInt32 muted = 0;
    469   UInt32 size = sizeof(muted);
    470   OSStatus result = AudioObjectGetPropertyData(
    471       input_device_id_, &property_address, 0, NULL, &size, &muted);
    472   DLOG_IF(WARNING, result != noErr) << "Failed to get mute state";
    473   return result == noErr && muted != 0;
    474 }
    475 
    476 // AUHAL AudioDeviceOutput unit callback
    477 OSStatus AUAudioInputStream::InputProc(void* user_data,
    478                                        AudioUnitRenderActionFlags* flags,
    479                                        const AudioTimeStamp* time_stamp,
    480                                        UInt32 bus_number,
    481                                        UInt32 number_of_frames,
    482                                        AudioBufferList* io_data) {
    483   // Verify that the correct bus is used (Input bus/Element 1)
    484   DCHECK_EQ(bus_number, static_cast<UInt32>(1));
    485   AUAudioInputStream* audio_input =
    486       reinterpret_cast<AUAudioInputStream*>(user_data);
    487   DCHECK(audio_input);
    488   if (!audio_input)
    489     return kAudioUnitErr_InvalidElement;
    490 
    491   // Receive audio from the AUHAL from the output scope of the Audio Unit.
    492   OSStatus result = AudioUnitRender(audio_input->audio_unit(),
    493                                     flags,
    494                                     time_stamp,
    495                                     bus_number,
    496                                     number_of_frames,
    497                                     audio_input->audio_buffer_list());
    498   if (result)
    499     return result;
    500 
    501   // Deliver recorded data to the consumer as a callback.
    502   return audio_input->Provide(number_of_frames,
    503                               audio_input->audio_buffer_list(),
    504                               time_stamp);
    505 }
    506 
    507 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
    508                                      AudioBufferList* io_data,
    509                                      const AudioTimeStamp* time_stamp) {
    510   // Update the capture latency.
    511   double capture_latency_frames = GetCaptureLatency(time_stamp);
    512 
    513   // The AGC volume level is updated once every second on a separate thread.
    514   // Note that, |volume| is also updated each time SetVolume() is called
    515   // through IPC by the render-side AGC.
    516   double normalized_volume = 0.0;
    517   GetAgcVolume(&normalized_volume);
    518 
    519   AudioBuffer& buffer = io_data->mBuffers[0];
    520   uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
    521   uint32 capture_delay_bytes = static_cast<uint32>
    522       ((capture_latency_frames + 0.5) * format_.mBytesPerFrame);
    523   DCHECK(audio_data);
    524   if (!audio_data)
    525     return kAudioUnitErr_InvalidElement;
    526 
    527   // Copy captured (and interleaved) data into FIFO.
    528   fifo_.Push(audio_data, number_of_frames, format_.mBitsPerChannel / 8);
    529 
    530   // Consume and deliver the data when the FIFO has a block of available data.
    531   while (fifo_.available_blocks()) {
    532     const AudioBus* audio_bus = fifo_.Consume();
    533     DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_));
    534 
    535     // Compensate the audio delay caused by the FIFO.
    536     capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame;
    537     sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume);
    538   }
    539 
    540   return noErr;
    541 }
    542 
    543 int AUAudioInputStream::HardwareSampleRate() {
    544   // Determine the default input device's sample-rate.
    545   AudioDeviceID device_id = kAudioObjectUnknown;
    546   UInt32 info_size = sizeof(device_id);
    547 
    548   AudioObjectPropertyAddress default_input_device_address = {
    549     kAudioHardwarePropertyDefaultInputDevice,
    550     kAudioObjectPropertyScopeGlobal,
    551     kAudioObjectPropertyElementMaster
    552   };
    553   OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
    554                                                &default_input_device_address,
    555                                                0,
    556                                                0,
    557                                                &info_size,
    558                                                &device_id);
    559   if (result != noErr)
    560     return 0.0;
    561 
    562   Float64 nominal_sample_rate;
    563   info_size = sizeof(nominal_sample_rate);
    564 
    565   AudioObjectPropertyAddress nominal_sample_rate_address = {
    566     kAudioDevicePropertyNominalSampleRate,
    567     kAudioObjectPropertyScopeGlobal,
    568     kAudioObjectPropertyElementMaster
    569   };
    570   result = AudioObjectGetPropertyData(device_id,
    571                                       &nominal_sample_rate_address,
    572                                       0,
    573                                       0,
    574                                       &info_size,
    575                                       &nominal_sample_rate);
    576   if (result != noErr)
    577     return 0.0;
    578 
    579   return static_cast<int>(nominal_sample_rate);
    580 }
    581 
    582 double AUAudioInputStream::GetHardwareLatency() {
    583   if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) {
    584     DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
    585     return 0.0;
    586   }
    587 
    588   // Get audio unit latency.
    589   Float64 audio_unit_latency_sec = 0.0;
    590   UInt32 size = sizeof(audio_unit_latency_sec);
    591   OSStatus result = AudioUnitGetProperty(audio_unit_,
    592                                          kAudioUnitProperty_Latency,
    593                                          kAudioUnitScope_Global,
    594                                          0,
    595                                          &audio_unit_latency_sec,
    596                                          &size);
    597   OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
    598       << "Could not get audio unit latency";
    599 
    600   // Get input audio device latency.
    601   AudioObjectPropertyAddress property_address = {
    602     kAudioDevicePropertyLatency,
    603     kAudioDevicePropertyScopeInput,
    604     kAudioObjectPropertyElementMaster
    605   };
    606   UInt32 device_latency_frames = 0;
    607   size = sizeof(device_latency_frames);
    608   result = AudioObjectGetPropertyData(input_device_id_,
    609                                       &property_address,
    610                                       0,
    611                                       NULL,
    612                                       &size,
    613                                       &device_latency_frames);
    614   DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
    615 
    616   return static_cast<double>((audio_unit_latency_sec *
    617       format_.mSampleRate) + device_latency_frames);
    618 }
    619 
    620 double AUAudioInputStream::GetCaptureLatency(
    621     const AudioTimeStamp* input_time_stamp) {
    622   // Get the delay between between the actual recording instant and the time
    623   // when the data packet is provided as a callback.
    624   UInt64 capture_time_ns = AudioConvertHostTimeToNanos(
    625       input_time_stamp->mHostTime);
    626   UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
    627   double delay_frames = static_cast<double>
    628       (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate);
    629 
    630   // Total latency is composed by the dynamic latency and the fixed
    631   // hardware latency.
    632   return (delay_frames + hardware_latency_frames_);
    633 }
    634 
    635 int AUAudioInputStream::GetNumberOfChannelsFromStream() {
    636   // Get the stream format, to be able to read the number of channels.
    637   AudioObjectPropertyAddress property_address = {
    638     kAudioDevicePropertyStreamFormat,
    639     kAudioDevicePropertyScopeInput,
    640     kAudioObjectPropertyElementMaster
    641   };
    642   AudioStreamBasicDescription stream_format;
    643   UInt32 size = sizeof(stream_format);
    644   OSStatus result = AudioObjectGetPropertyData(input_device_id_,
    645                                                &property_address,
    646                                                0,
    647                                                NULL,
    648                                                &size,
    649                                                &stream_format);
    650   if (result != noErr) {
    651     DLOG(WARNING) << "Could not get stream format";
    652     return 0;
    653   }
    654 
    655   return static_cast<int>(stream_format.mChannelsPerFrame);
    656 }
    657 
    658 void AUAudioInputStream::HandleError(OSStatus err) {
    659   NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
    660                << " (" << err << ")";
    661   if (sink_)
    662     sink_->OnError(this);
    663 }
    664 
    665 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
    666   Boolean is_settable = false;
    667   AudioObjectPropertyAddress property_address = {
    668     kAudioDevicePropertyVolumeScalar,
    669     kAudioDevicePropertyScopeInput,
    670     static_cast<UInt32>(channel)
    671   };
    672   OSStatus result = AudioObjectIsPropertySettable(input_device_id_,
    673                                                   &property_address,
    674                                                   &is_settable);
    675   return (result == noErr) ? is_settable : false;
    676 }
    677 
    678 }  // namespace media
    679