Home | History | Annotate | Download | only in mac
      1 // Copyright 2013 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_auhal_mac.h"
      6 
      7 #include <CoreServices/CoreServices.h>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/debug/trace_event.h"
     13 #include "base/logging.h"
     14 #include "base/mac/mac_logging.h"
     15 #include "base/time/time.h"
     16 #include "media/audio/mac/audio_manager_mac.h"
     17 #include "media/base/audio_pull_fifo.h"
     18 
     19 namespace media {
     20 
     21 static void WrapBufferList(AudioBufferList* buffer_list,
     22                            AudioBus* bus,
     23                            int frames) {
     24   DCHECK(buffer_list);
     25   DCHECK(bus);
     26   const int channels = bus->channels();
     27   const int buffer_list_channels = buffer_list->mNumberBuffers;
     28   CHECK_EQ(channels, buffer_list_channels);
     29 
     30   // Copy pointers from AudioBufferList.
     31   for (int i = 0; i < channels; ++i) {
     32     bus->SetChannelData(
     33         i, static_cast<float*>(buffer_list->mBuffers[i].mData));
     34   }
     35 
     36   // Finally set the actual length.
     37   bus->set_frames(frames);
     38 }
     39 
     40 AUHALStream::AUHALStream(
     41     AudioManagerMac* manager,
     42     const AudioParameters& params,
     43     AudioDeviceID device)
     44     : manager_(manager),
     45       params_(params),
     46       output_channels_(params_.channels()),
     47       number_of_frames_(params_.frames_per_buffer()),
     48       source_(NULL),
     49       device_(device),
     50       audio_unit_(0),
     51       volume_(1),
     52       hardware_latency_frames_(0),
     53       stopped_(false),
     54       current_hardware_pending_bytes_(0) {
     55   // We must have a manager.
     56   DCHECK(manager_);
     57 
     58   VLOG(1) << "AUHALStream::AUHALStream()";
     59   VLOG(1) << "Device: " << device;
     60   VLOG(1) << "Output channels: " << output_channels_;
     61   VLOG(1) << "Sample rate: " << params_.sample_rate();
     62   VLOG(1) << "Buffer size: " << number_of_frames_;
     63 }
     64 
     65 AUHALStream::~AUHALStream() {
     66 }
     67 
     68 bool AUHALStream::Open() {
     69   // Get the total number of output channels that the
     70   // hardware supports.
     71   int device_output_channels;
     72   bool got_output_channels = AudioManagerMac::GetDeviceChannels(
     73       device_,
     74       kAudioDevicePropertyScopeOutput,
     75       &device_output_channels);
     76 
     77   // Sanity check the requested output channels.
     78   if (!got_output_channels ||
     79       output_channels_ <= 0 || output_channels_ > device_output_channels) {
     80     LOG(ERROR) << "AudioDevice does not support requested output channels.";
     81     return false;
     82   }
     83 
     84   // The requested sample-rate must match the hardware sample-rate.
     85   int sample_rate = AudioManagerMac::HardwareSampleRateForDevice(device_);
     86 
     87   if (sample_rate != params_.sample_rate()) {
     88     LOG(ERROR) << "Requested sample-rate: " << params_.sample_rate()
     89                << " must match the hardware sample-rate: " << sample_rate;
     90     return false;
     91   }
     92 
     93   // The output bus will wrap the AudioBufferList given to us in
     94   // the Render() callback.
     95   DCHECK_GT(output_channels_, 0);
     96   output_bus_ = AudioBus::CreateWrapper(output_channels_);
     97 
     98   bool configured = ConfigureAUHAL();
     99   if (configured)
    100     hardware_latency_frames_ = GetHardwareLatency();
    101 
    102   return configured;
    103 }
    104 
    105 void AUHALStream::Close() {
    106   if (audio_unit_) {
    107     OSStatus result = AudioUnitUninitialize(audio_unit_);
    108     OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
    109         << "AudioUnitUninitialize() failed.";
    110     result = AudioComponentInstanceDispose(audio_unit_);
    111     OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
    112         << "AudioComponentInstanceDispose() failed.";
    113   }
    114 
    115   // Inform the audio manager that we have been closed. This will cause our
    116   // destruction.
    117   manager_->ReleaseOutputStream(this);
    118 }
    119 
    120 void AUHALStream::Start(AudioSourceCallback* callback) {
    121   DCHECK(callback);
    122   if (!audio_unit_) {
    123     DLOG(ERROR) << "Open() has not been called successfully";
    124     return;
    125   }
    126 
    127   // Check if we should defer Start() for http://crbug.com/160920.
    128   if (manager_->ShouldDeferStreamStart()) {
    129     // Use a cancellable closure so that if Stop() is called before Start()
    130     // actually runs, we can cancel the pending start.
    131     deferred_start_cb_.Reset(
    132         base::Bind(&AUHALStream::Start, base::Unretained(this), callback));
    133     manager_->GetTaskRunner()->PostDelayedTask(
    134         FROM_HERE, deferred_start_cb_.callback(), base::TimeDelta::FromSeconds(
    135             AudioManagerMac::kStartDelayInSecsForPowerEvents));
    136     return;
    137   }
    138 
    139   stopped_ = false;
    140   audio_fifo_.reset();
    141   {
    142     base::AutoLock auto_lock(source_lock_);
    143     source_ = callback;
    144   }
    145 
    146   OSStatus result = AudioOutputUnitStart(audio_unit_);
    147   if (result == noErr)
    148     return;
    149 
    150   Stop();
    151   OSSTATUS_DLOG(ERROR, result) << "AudioOutputUnitStart() failed.";
    152   callback->OnError(this);
    153 }
    154 
    155 void AUHALStream::Stop() {
    156   deferred_start_cb_.Cancel();
    157   if (stopped_)
    158     return;
    159 
    160   OSStatus result = AudioOutputUnitStop(audio_unit_);
    161   OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
    162       << "AudioOutputUnitStop() failed.";
    163   if (result != noErr)
    164     source_->OnError(this);
    165 
    166   base::AutoLock auto_lock(source_lock_);
    167   source_ = NULL;
    168   stopped_ = true;
    169 }
    170 
    171 void AUHALStream::SetVolume(double volume) {
    172   volume_ = static_cast<float>(volume);
    173 }
    174 
    175 void AUHALStream::GetVolume(double* volume) {
    176   *volume = volume_;
    177 }
    178 
    179 // Pulls on our provider to get rendered audio stream.
    180 // Note to future hackers of this function: Do not add locks which can
    181 // be contended in the middle of stream processing here (starting and stopping
    182 // the stream are ok) because this is running on a real-time thread.
    183 OSStatus AUHALStream::Render(
    184     AudioUnitRenderActionFlags* flags,
    185     const AudioTimeStamp* output_time_stamp,
    186     UInt32 bus_number,
    187     UInt32 number_of_frames,
    188     AudioBufferList* data) {
    189   TRACE_EVENT0("audio", "AUHALStream::Render");
    190 
    191   // If the stream parameters change for any reason, we need to insert a FIFO
    192   // since the OnMoreData() pipeline can't handle frame size changes.
    193   if (number_of_frames != number_of_frames_) {
    194     // Create a FIFO on the fly to handle any discrepancies in callback rates.
    195     if (!audio_fifo_) {
    196       VLOG(1) << "Audio frame size changed from " << number_of_frames_ << " to "
    197               << number_of_frames << "; adding FIFO to compensate.";
    198       audio_fifo_.reset(new AudioPullFifo(
    199           output_channels_,
    200           number_of_frames_,
    201           base::Bind(&AUHALStream::ProvideInput, base::Unretained(this))));
    202     }
    203   }
    204 
    205   // Make |output_bus_| wrap the output AudioBufferList.
    206   WrapBufferList(data, output_bus_.get(), number_of_frames);
    207 
    208   // Update the playout latency.
    209   const double playout_latency_frames = GetPlayoutLatency(output_time_stamp);
    210   current_hardware_pending_bytes_ = static_cast<uint32>(
    211       (playout_latency_frames + 0.5) * params_.GetBytesPerFrame());
    212 
    213   if (audio_fifo_)
    214     audio_fifo_->Consume(output_bus_.get(), output_bus_->frames());
    215   else
    216     ProvideInput(0, output_bus_.get());
    217 
    218   return noErr;
    219 }
    220 
    221 void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) {
    222   base::AutoLock auto_lock(source_lock_);
    223   if (!source_) {
    224     dest->Zero();
    225     return;
    226   }
    227 
    228   // Supply the input data and render the output data.
    229   source_->OnMoreData(
    230       dest,
    231       AudioBuffersState(0,
    232                         current_hardware_pending_bytes_ +
    233                             frame_delay * params_.GetBytesPerFrame()));
    234   dest->Scale(volume_);
    235 }
    236 
    237 // AUHAL callback.
    238 OSStatus AUHALStream::InputProc(
    239     void* user_data,
    240     AudioUnitRenderActionFlags* flags,
    241     const AudioTimeStamp* output_time_stamp,
    242     UInt32 bus_number,
    243     UInt32 number_of_frames,
    244     AudioBufferList* io_data) {
    245   // Dispatch to our class method.
    246   AUHALStream* audio_output =
    247       static_cast<AUHALStream*>(user_data);
    248   if (!audio_output)
    249     return -1;
    250 
    251   return audio_output->Render(
    252       flags,
    253       output_time_stamp,
    254       bus_number,
    255       number_of_frames,
    256       io_data);
    257 }
    258 
    259 double AUHALStream::GetHardwareLatency() {
    260   if (!audio_unit_ || device_ == kAudioObjectUnknown) {
    261     DLOG(WARNING) << "AudioUnit is NULL or device ID is unknown";
    262     return 0.0;
    263   }
    264 
    265   // Get audio unit latency.
    266   Float64 audio_unit_latency_sec = 0.0;
    267   UInt32 size = sizeof(audio_unit_latency_sec);
    268   OSStatus result = AudioUnitGetProperty(
    269       audio_unit_,
    270       kAudioUnitProperty_Latency,
    271       kAudioUnitScope_Global,
    272       0,
    273       &audio_unit_latency_sec,
    274       &size);
    275   if (result != noErr) {
    276     OSSTATUS_DLOG(WARNING, result) << "Could not get AudioUnit latency";
    277     return 0.0;
    278   }
    279 
    280   // Get output audio device latency.
    281   static const AudioObjectPropertyAddress property_address = {
    282     kAudioDevicePropertyLatency,
    283     kAudioDevicePropertyScopeOutput,
    284     kAudioObjectPropertyElementMaster
    285   };
    286 
    287   UInt32 device_latency_frames = 0;
    288   size = sizeof(device_latency_frames);
    289   result = AudioObjectGetPropertyData(
    290       device_,
    291       &property_address,
    292       0,
    293       NULL,
    294       &size,
    295       &device_latency_frames);
    296   if (result != noErr) {
    297     OSSTATUS_DLOG(WARNING, result) << "Could not get audio device latency";
    298     return 0.0;
    299   }
    300 
    301   return static_cast<double>((audio_unit_latency_sec *
    302       output_format_.mSampleRate) + device_latency_frames);
    303 }
    304 
    305 double AUHALStream::GetPlayoutLatency(
    306     const AudioTimeStamp* output_time_stamp) {
    307   // Ensure mHostTime is valid.
    308   if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0)
    309     return 0;
    310 
    311   // Get the delay between the moment getting the callback and the scheduled
    312   // time stamp that tells when the data is going to be played out.
    313   UInt64 output_time_ns = AudioConvertHostTimeToNanos(
    314       output_time_stamp->mHostTime);
    315   UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
    316 
    317   // Prevent overflow leading to huge delay information; occurs regularly on
    318   // the bots, probably less so in the wild.
    319   if (now_ns > output_time_ns)
    320     return 0;
    321 
    322   double delay_frames = static_cast<double>
    323       (1e-9 * (output_time_ns - now_ns) * output_format_.mSampleRate);
    324 
    325   return (delay_frames + hardware_latency_frames_);
    326 }
    327 
    328 bool AUHALStream::SetStreamFormat(
    329     AudioStreamBasicDescription* desc,
    330     int channels,
    331     UInt32 scope,
    332     UInt32 element) {
    333   DCHECK(desc);
    334   AudioStreamBasicDescription& format = *desc;
    335 
    336   format.mSampleRate = params_.sample_rate();
    337   format.mFormatID = kAudioFormatLinearPCM;
    338   format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked |
    339       kLinearPCMFormatFlagIsNonInterleaved;
    340   format.mBytesPerPacket = sizeof(Float32);
    341   format.mFramesPerPacket = 1;
    342   format.mBytesPerFrame = sizeof(Float32);
    343   format.mChannelsPerFrame = channels;
    344   format.mBitsPerChannel = 32;
    345   format.mReserved = 0;
    346 
    347   OSStatus result = AudioUnitSetProperty(
    348       audio_unit_,
    349       kAudioUnitProperty_StreamFormat,
    350       scope,
    351       element,
    352       &format,
    353       sizeof(format));
    354   return (result == noErr);
    355 }
    356 
    357 bool AUHALStream::ConfigureAUHAL() {
    358   if (device_ == kAudioObjectUnknown || output_channels_ == 0)
    359     return false;
    360 
    361   AudioComponentDescription desc = {
    362       kAudioUnitType_Output,
    363       kAudioUnitSubType_HALOutput,
    364       kAudioUnitManufacturer_Apple,
    365       0,
    366       0
    367   };
    368   AudioComponent comp = AudioComponentFindNext(0, &desc);
    369   if (!comp)
    370     return false;
    371 
    372   OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
    373   if (result != noErr) {
    374     OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed.";
    375     return false;
    376   }
    377 
    378   // Enable output as appropriate.
    379   // See Apple technote for details about the EnableIO property.
    380   // Note that we use bus 1 for input and bus 0 for output:
    381   // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
    382   UInt32 enable_IO = 1;
    383   result = AudioUnitSetProperty(
    384       audio_unit_,
    385       kAudioOutputUnitProperty_EnableIO,
    386       kAudioUnitScope_Output,
    387       0,
    388       &enable_IO,
    389       sizeof(enable_IO));
    390   if (result != noErr)
    391     return false;
    392 
    393   // Set the device to be used with the AUHAL AudioUnit.
    394   result = AudioUnitSetProperty(
    395       audio_unit_,
    396       kAudioOutputUnitProperty_CurrentDevice,
    397       kAudioUnitScope_Global,
    398       0,
    399       &device_,
    400       sizeof(AudioDeviceID));
    401   if (result != noErr)
    402     return false;
    403 
    404   // Set stream formats.
    405   // See Apple's tech note for details on the peculiar way that
    406   // inputs and outputs are handled in the AUHAL concerning scope and bus
    407   // (element) numbers:
    408   // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
    409 
    410   if (!SetStreamFormat(&output_format_,
    411                        output_channels_,
    412                        kAudioUnitScope_Input,
    413                        0)) {
    414     return false;
    415   }
    416 
    417   // Set the buffer frame size.
    418   // WARNING: Setting this value changes the frame size for all output audio
    419   // units in the current process.  As a result, the AURenderCallback must be
    420   // able to handle arbitrary buffer sizes and FIFO appropriately.
    421   UInt32 buffer_size = 0;
    422   UInt32 property_size = sizeof(buffer_size);
    423   result = AudioUnitGetProperty(audio_unit_,
    424                                 kAudioDevicePropertyBufferFrameSize,
    425                                 kAudioUnitScope_Output,
    426                                 0,
    427                                 &buffer_size,
    428                                 &property_size);
    429   if (result != noErr) {
    430     OSSTATUS_DLOG(ERROR, result)
    431         << "AudioUnitGetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
    432     return false;
    433   }
    434 
    435   // Only set the buffer size if we're the only active stream or the buffer size
    436   // is lower than the current buffer size.
    437   if (manager_->output_stream_count() == 1 || number_of_frames_ < buffer_size) {
    438     buffer_size = number_of_frames_;
    439     result = AudioUnitSetProperty(audio_unit_,
    440                                   kAudioDevicePropertyBufferFrameSize,
    441                                   kAudioUnitScope_Output,
    442                                   0,
    443                                   &buffer_size,
    444                                   sizeof(buffer_size));
    445     if (result != noErr) {
    446       OSSTATUS_DLOG(ERROR, result) << "AudioUnitSetProperty("
    447                                       "kAudioDevicePropertyBufferFrameSize) "
    448                                       "failed.  Size: " << number_of_frames_;
    449       return false;
    450     }
    451   }
    452 
    453   // Setup callback.
    454   AURenderCallbackStruct callback;
    455   callback.inputProc = InputProc;
    456   callback.inputProcRefCon = this;
    457   result = AudioUnitSetProperty(
    458       audio_unit_,
    459       kAudioUnitProperty_SetRenderCallback,
    460       kAudioUnitScope_Input,
    461       0,
    462       &callback,
    463       sizeof(callback));
    464   if (result != noErr)
    465     return false;
    466 
    467   result = AudioUnitInitialize(audio_unit_);
    468   if (result != noErr) {
    469     OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed.";
    470     return false;
    471   }
    472 
    473   return true;
    474 }
    475 
    476 }  // namespace media
    477