| OLD | NEW |
| (Empty) |
| 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_output_mac.h" | |
| 6 | |
| 7 #include <CoreServices/CoreServices.h> | |
| 8 | |
| 9 #include "base/basictypes.h" | |
| 10 #include "base/command_line.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/mac/mac_logging.h" | |
| 13 #include "media/audio/mac/audio_manager_mac.h" | |
| 14 #include "media/base/media_switches.h" | |
| 15 | |
| 16 namespace media { | |
| 17 | |
| 18 static std::ostream& operator<<(std::ostream& os, | |
| 19 const AudioStreamBasicDescription& format) { | |
| 20 os << "sample rate : " << format.mSampleRate << std::endl | |
| 21 << "format ID : " << format.mFormatID << std::endl | |
| 22 << "format flags : " << format.mFormatFlags << std::endl | |
| 23 << "bytes per packet : " << format.mBytesPerPacket << std::endl | |
| 24 << "frames per packet : " << format.mFramesPerPacket << std::endl | |
| 25 << "bytes per frame : " << format.mBytesPerFrame << std::endl | |
| 26 << "channels per frame: " << format.mChannelsPerFrame << std::endl | |
| 27 << "bits per channel : " << format.mBitsPerChannel; | |
| 28 return os; | |
| 29 } | |
| 30 | |
| 31 static AudioObjectPropertyAddress kDefaultOutputDeviceAddress = { | |
| 32 kAudioHardwarePropertyDefaultOutputDevice, | |
| 33 kAudioObjectPropertyScopeGlobal, | |
| 34 kAudioObjectPropertyElementMaster | |
| 35 }; | |
| 36 | |
| 37 // Overview of operation: | |
| 38 // 1) An object of AUAudioOutputStream is created by the AudioManager | |
| 39 // factory: audio_man->MakeAudioStream(). | |
| 40 // 2) Next some thread will call Open(), at that point the underlying | |
| 41 // default output Audio Unit is created and configured. | |
| 42 // 3) Then some thread will call Start(source). | |
| 43 // Then the Audio Unit is started which creates its own thread which | |
| 44 // periodically will call the source for more data as buffers are being | |
| 45 // consumed. | |
| 46 // 4) At some point some thread will call Stop(), which we handle by directly | |
| 47 // stopping the default output Audio Unit. | |
| 48 // 6) The same thread that called stop will call Close() where we cleanup | |
| 49 // and notify the audio manager, which likely will destroy this object. | |
| 50 | |
| 51 AUAudioOutputStream::AUAudioOutputStream( | |
| 52 AudioManagerMac* manager, const AudioParameters& params) | |
| 53 : manager_(manager), | |
| 54 source_(NULL), | |
| 55 output_unit_(0), | |
| 56 output_device_id_(kAudioObjectUnknown), | |
| 57 volume_(1), | |
| 58 hardware_latency_frames_(0), | |
| 59 stopped_(false), | |
| 60 audio_bus_(AudioBus::Create(params)) { | |
| 61 // We must have a manager. | |
| 62 DCHECK(manager_); | |
| 63 | |
| 64 // A frame is one sample across all channels. In interleaved audio the per | |
| 65 // frame fields identify the set of n |channels|. In uncompressed audio, a | |
| 66 // packet is always one frame. | |
| 67 format_.mSampleRate = params.sample_rate(); | |
| 68 format_.mFormatID = kAudioFormatLinearPCM; | |
| 69 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | |
| 70 kLinearPCMFormatFlagIsSignedInteger; | |
| 71 format_.mBitsPerChannel = params.bits_per_sample(); | |
| 72 format_.mChannelsPerFrame = params.channels(); | |
| 73 format_.mFramesPerPacket = 1; | |
| 74 format_.mBytesPerPacket = (format_.mBitsPerChannel * params.channels()) / 8; | |
| 75 format_.mBytesPerFrame = format_.mBytesPerPacket; | |
| 76 format_.mReserved = 0; | |
| 77 | |
| 78 DVLOG(1) << "Desired ouput format: " << format_; | |
| 79 | |
| 80 // Calculate the number of sample frames per callback. | |
| 81 number_of_frames_ = params.frames_per_buffer(); | |
| 82 DVLOG(1) << "Number of frames per callback: " << number_of_frames_; | |
| 83 } | |
| 84 | |
| 85 AUAudioOutputStream::~AUAudioOutputStream() { | |
| 86 } | |
| 87 | |
| 88 bool AUAudioOutputStream::Open() { | |
| 89 // Obtain the current input device selected by the user. | |
| 90 UInt32 size = sizeof(output_device_id_); | |
| 91 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, | |
| 92 &kDefaultOutputDeviceAddress, | |
| 93 0, | |
| 94 0, | |
| 95 &size, | |
| 96 &output_device_id_); | |
| 97 if (result != noErr || output_device_id_ == kAudioObjectUnknown) { | |
| 98 OSSTATUS_DLOG(ERROR, result) | |
| 99 << "Could not get default audio output device."; | |
| 100 return false; | |
| 101 } | |
| 102 | |
| 103 // Open and initialize the DefaultOutputUnit. | |
| 104 AudioComponent comp; | |
| 105 AudioComponentDescription desc; | |
| 106 | |
| 107 desc.componentType = kAudioUnitType_Output; | |
| 108 desc.componentSubType = kAudioUnitSubType_DefaultOutput; | |
| 109 desc.componentManufacturer = kAudioUnitManufacturer_Apple; | |
| 110 desc.componentFlags = 0; | |
| 111 desc.componentFlagsMask = 0; | |
| 112 comp = AudioComponentFindNext(0, &desc); | |
| 113 if (!comp) | |
| 114 return false; | |
| 115 | |
| 116 result = AudioComponentInstanceNew(comp, &output_unit_); | |
| 117 if (result != noErr) { | |
| 118 OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed."; | |
| 119 return false; | |
| 120 } | |
| 121 | |
| 122 result = AudioUnitInitialize(output_unit_); | |
| 123 if (result != noErr) { | |
| 124 OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed."; | |
| 125 return false; | |
| 126 } | |
| 127 | |
| 128 hardware_latency_frames_ = GetHardwareLatency(); | |
| 129 | |
| 130 return Configure(); | |
| 131 } | |
| 132 | |
| 133 bool AUAudioOutputStream::Configure() { | |
| 134 // Set the render callback. | |
| 135 AURenderCallbackStruct input; | |
| 136 input.inputProc = InputProc; | |
| 137 input.inputProcRefCon = this; | |
| 138 OSStatus result = AudioUnitSetProperty( | |
| 139 output_unit_, | |
| 140 kAudioUnitProperty_SetRenderCallback, | |
| 141 kAudioUnitScope_Global, | |
| 142 0, | |
| 143 &input, | |
| 144 sizeof(input)); | |
| 145 if (result != noErr) { | |
| 146 OSSTATUS_DLOG(ERROR, result) | |
| 147 << "AudioUnitSetProperty(kAudioUnitProperty_SetRenderCallback) failed."; | |
| 148 return false; | |
| 149 } | |
| 150 | |
| 151 // Set the stream format. | |
| 152 result = AudioUnitSetProperty( | |
| 153 output_unit_, | |
| 154 kAudioUnitProperty_StreamFormat, | |
| 155 kAudioUnitScope_Input, | |
| 156 0, | |
| 157 &format_, | |
| 158 sizeof(format_)); | |
| 159 if (result != noErr) { | |
| 160 OSSTATUS_DLOG(ERROR, result) | |
| 161 << "AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed."; | |
| 162 return false; | |
| 163 } | |
| 164 | |
| 165 // Set the buffer frame size. | |
| 166 // WARNING: Setting this value changes the frame size for all audio units in | |
| 167 // the current process. It's imperative that the input and output frame sizes | |
| 168 // be the same as the frames_per_buffer() returned by | |
| 169 // GetDefaultOutputStreamParameters. | |
| 170 // See http://crbug.com/154352 for details. | |
| 171 const AudioParameters hw_params = | |
| 172 manager_->GetDefaultOutputStreamParameters(); | |
| 173 if (number_of_frames_ != static_cast<size_t>(hw_params.frames_per_buffer())) { | |
| 174 DLOG(ERROR) << "Audio buffer size does not match hardware buffer size."; | |
| 175 return false; | |
| 176 } | |
| 177 | |
| 178 UInt32 buffer_size = number_of_frames_; | |
| 179 result = AudioUnitSetProperty( | |
| 180 output_unit_, | |
| 181 kAudioDevicePropertyBufferFrameSize, | |
| 182 kAudioUnitScope_Output, | |
| 183 0, | |
| 184 &buffer_size, | |
| 185 sizeof(buffer_size)); | |
| 186 if (result != noErr) { | |
| 187 OSSTATUS_DLOG(ERROR, result) | |
| 188 << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed."; | |
| 189 return false; | |
| 190 } | |
| 191 | |
| 192 return true; | |
| 193 } | |
| 194 | |
| 195 void AUAudioOutputStream::Close() { | |
| 196 if (output_unit_) | |
| 197 AudioComponentInstanceDispose(output_unit_); | |
| 198 | |
| 199 // Inform the audio manager that we have been closed. This can cause our | |
| 200 // destruction. | |
| 201 manager_->ReleaseOutputStream(this); | |
| 202 } | |
| 203 | |
| 204 void AUAudioOutputStream::Start(AudioSourceCallback* callback) { | |
| 205 DCHECK(callback); | |
| 206 if (!output_unit_) { | |
| 207 DLOG(ERROR) << "Open() has not been called successfully"; | |
| 208 return; | |
| 209 } | |
| 210 | |
| 211 stopped_ = false; | |
| 212 { | |
| 213 base::AutoLock auto_lock(source_lock_); | |
| 214 source_ = callback; | |
| 215 } | |
| 216 | |
| 217 AudioOutputUnitStart(output_unit_); | |
| 218 } | |
| 219 | |
| 220 void AUAudioOutputStream::Stop() { | |
| 221 if (stopped_) | |
| 222 return; | |
| 223 | |
| 224 AudioOutputUnitStop(output_unit_); | |
| 225 | |
| 226 base::AutoLock auto_lock(source_lock_); | |
| 227 source_ = NULL; | |
| 228 stopped_ = true; | |
| 229 } | |
| 230 | |
| 231 void AUAudioOutputStream::SetVolume(double volume) { | |
| 232 if (!output_unit_) | |
| 233 return; | |
| 234 volume_ = static_cast<float>(volume); | |
| 235 | |
| 236 // TODO(crogers): set volume property | |
| 237 } | |
| 238 | |
| 239 void AUAudioOutputStream::GetVolume(double* volume) { | |
| 240 if (!output_unit_) | |
| 241 return; | |
| 242 *volume = volume_; | |
| 243 } | |
| 244 | |
| 245 // Pulls on our provider to get rendered audio stream. | |
| 246 // Note to future hackers of this function: Do not add locks here because this | |
| 247 // is running on a real-time thread (for low-latency). | |
| 248 OSStatus AUAudioOutputStream::Render(UInt32 number_of_frames, | |
| 249 AudioBufferList* io_data, | |
| 250 const AudioTimeStamp* output_time_stamp) { | |
| 251 // Update the playout latency. | |
| 252 double playout_latency_frames = GetPlayoutLatency(output_time_stamp); | |
| 253 | |
| 254 AudioBuffer& buffer = io_data->mBuffers[0]; | |
| 255 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); | |
| 256 uint32 hardware_pending_bytes = static_cast<uint32> | |
| 257 ((playout_latency_frames + 0.5) * format_.mBytesPerFrame); | |
| 258 | |
| 259 // Unfortunately AUAudioInputStream and AUAudioOutputStream share the frame | |
| 260 // size set by kAudioDevicePropertyBufferFrameSize above on a per process | |
| 261 // basis. What this means is that the |number_of_frames| value may be larger | |
| 262 // or smaller than the value set during Configure(). In this case either | |
| 263 // audio input or audio output will be broken, so just output silence. | |
| 264 // TODO(crogers): Figure out what can trigger a change in |number_of_frames|. | |
| 265 // See http://crbug.com/154352 for details. | |
| 266 if (number_of_frames != static_cast<UInt32>(audio_bus_->frames())) { | |
| 267 memset(audio_data, 0, number_of_frames * format_.mBytesPerFrame); | |
| 268 return noErr; | |
| 269 } | |
| 270 | |
| 271 int frames_filled = 0; | |
| 272 { | |
| 273 // Render() shouldn't be called except between AudioOutputUnitStart() and | |
| 274 // AudioOutputUnitStop() calls, but crash reports have shown otherwise: | |
| 275 // http://crbug.com/178765. We use |source_lock_| to prevent races and | |
| 276 // crashes in Render() when |source_| is cleared. | |
| 277 base::AutoLock auto_lock(source_lock_); | |
| 278 if (!source_) { | |
| 279 memset(audio_data, 0, number_of_frames * format_.mBytesPerFrame); | |
| 280 return noErr; | |
| 281 } | |
| 282 | |
| 283 frames_filled = source_->OnMoreData( | |
| 284 audio_bus_.get(), AudioBuffersState(0, hardware_pending_bytes)); | |
| 285 } | |
| 286 | |
| 287 // Note: If this ever changes to output raw float the data must be clipped and | |
| 288 // sanitized since it may come from an untrusted source such as NaCl. | |
| 289 audio_bus_->Scale(volume_); | |
| 290 audio_bus_->ToInterleaved( | |
| 291 frames_filled, format_.mBitsPerChannel / 8, audio_data); | |
| 292 | |
| 293 return noErr; | |
| 294 } | |
| 295 | |
| 296 // DefaultOutputUnit callback | |
| 297 OSStatus AUAudioOutputStream::InputProc(void* user_data, | |
| 298 AudioUnitRenderActionFlags*, | |
| 299 const AudioTimeStamp* output_time_stamp, | |
| 300 UInt32, | |
| 301 UInt32 number_of_frames, | |
| 302 AudioBufferList* io_data) { | |
| 303 AUAudioOutputStream* audio_output = | |
| 304 static_cast<AUAudioOutputStream*>(user_data); | |
| 305 if (!audio_output) | |
| 306 return -1; | |
| 307 | |
| 308 return audio_output->Render(number_of_frames, io_data, output_time_stamp); | |
| 309 } | |
| 310 | |
| 311 int AUAudioOutputStream::HardwareSampleRate() { | |
| 312 // Determine the default output device's sample-rate. | |
| 313 AudioDeviceID device_id = kAudioObjectUnknown; | |
| 314 UInt32 info_size = sizeof(device_id); | |
| 315 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, | |
| 316 &kDefaultOutputDeviceAddress, | |
| 317 0, | |
| 318 0, | |
| 319 &info_size, | |
| 320 &device_id); | |
| 321 if (result != noErr || device_id == kAudioObjectUnknown) { | |
| 322 OSSTATUS_DLOG(WARNING, result) | |
| 323 << "Could not get default audio output device."; | |
| 324 return 0; | |
| 325 } | |
| 326 | |
| 327 Float64 nominal_sample_rate; | |
| 328 info_size = sizeof(nominal_sample_rate); | |
| 329 | |
| 330 AudioObjectPropertyAddress nominal_sample_rate_address = { | |
| 331 kAudioDevicePropertyNominalSampleRate, | |
| 332 kAudioObjectPropertyScopeGlobal, | |
| 333 kAudioObjectPropertyElementMaster | |
| 334 }; | |
| 335 result = AudioObjectGetPropertyData(device_id, | |
| 336 &nominal_sample_rate_address, | |
| 337 0, | |
| 338 0, | |
| 339 &info_size, | |
| 340 &nominal_sample_rate); | |
| 341 if (result != noErr) { | |
| 342 OSSTATUS_DLOG(WARNING, result) | |
| 343 << "Could not get default sample rate for device: " << device_id; | |
| 344 return 0; | |
| 345 } | |
| 346 | |
| 347 return static_cast<int>(nominal_sample_rate); | |
| 348 } | |
| 349 | |
| 350 double AUAudioOutputStream::GetHardwareLatency() { | |
| 351 if (!output_unit_ || output_device_id_ == kAudioObjectUnknown) { | |
| 352 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown"; | |
| 353 return 0.0; | |
| 354 } | |
| 355 | |
| 356 // Get audio unit latency. | |
| 357 Float64 audio_unit_latency_sec = 0.0; | |
| 358 UInt32 size = sizeof(audio_unit_latency_sec); | |
| 359 OSStatus result = AudioUnitGetProperty(output_unit_, | |
| 360 kAudioUnitProperty_Latency, | |
| 361 kAudioUnitScope_Global, | |
| 362 0, | |
| 363 &audio_unit_latency_sec, | |
| 364 &size); | |
| 365 if (result != noErr) { | |
| 366 OSSTATUS_DLOG(WARNING, result) << "Could not get audio unit latency"; | |
| 367 return 0.0; | |
| 368 } | |
| 369 | |
| 370 // Get output audio device latency. | |
| 371 AudioObjectPropertyAddress property_address = { | |
| 372 kAudioDevicePropertyLatency, | |
| 373 kAudioDevicePropertyScopeOutput, | |
| 374 kAudioObjectPropertyElementMaster | |
| 375 }; | |
| 376 UInt32 device_latency_frames = 0; | |
| 377 size = sizeof(device_latency_frames); | |
| 378 result = AudioObjectGetPropertyData(output_device_id_, | |
| 379 &property_address, | |
| 380 0, | |
| 381 NULL, | |
| 382 &size, | |
| 383 &device_latency_frames); | |
| 384 if (result != noErr) { | |
| 385 OSSTATUS_DLOG(WARNING, result) << "Could not get audio unit latency"; | |
| 386 return 0.0; | |
| 387 } | |
| 388 | |
| 389 return static_cast<double>((audio_unit_latency_sec * | |
| 390 format_.mSampleRate) + device_latency_frames); | |
| 391 } | |
| 392 | |
| 393 double AUAudioOutputStream::GetPlayoutLatency( | |
| 394 const AudioTimeStamp* output_time_stamp) { | |
| 395 // Ensure mHostTime is valid. | |
| 396 if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0) | |
| 397 return 0; | |
| 398 | |
| 399 // Get the delay between the moment getting the callback and the scheduled | |
| 400 // time stamp that tells when the data is going to be played out. | |
| 401 UInt64 output_time_ns = AudioConvertHostTimeToNanos( | |
| 402 output_time_stamp->mHostTime); | |
| 403 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); | |
| 404 | |
| 405 // Prevent overflow leading to huge delay information; occurs regularly on | |
| 406 // the bots, probably less so in the wild. | |
| 407 if (now_ns > output_time_ns) | |
| 408 return 0; | |
| 409 | |
| 410 double delay_frames = static_cast<double> | |
| 411 (1e-9 * (output_time_ns - now_ns) * format_.mSampleRate); | |
| 412 | |
| 413 return (delay_frames + hardware_latency_frames_); | |
| 414 } | |
| 415 | |
| 416 } // namespace media | |
| OLD | NEW |