Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/mac/audio_low_latency_input_mac.h" | 5 #include "media/audio/mac/audio_low_latency_input_mac.h" |
| 6 | 6 |
| 7 #include <CoreServices/CoreServices.h> | 7 #include <CoreServices/CoreServices.h> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 | 24 |
| 25 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit" | 25 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit" |
| 26 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html | 26 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html |
| 27 // for more details and background regarding this implementation. | 27 // for more details and background regarding this implementation. |
| 28 | 28 |
| 29 AUAudioInputStream::AUAudioInputStream( | 29 AUAudioInputStream::AUAudioInputStream( |
| 30 AudioManagerMac* manager, const AudioParameters& params) | 30 AudioManagerMac* manager, const AudioParameters& params) |
| 31 : manager_(manager), | 31 : manager_(manager), |
| 32 sink_(NULL), | 32 sink_(NULL), |
| 33 audio_unit_(0), | 33 audio_unit_(0), |
| 34 started_(false) { | 34 input_device_id_(kAudioObjectUnknown), |
| 35 started_(false), | |
| 36 hardware_latency_frames_(0) { | |
| 35 DCHECK(manager_); | 37 DCHECK(manager_); |
| 36 | 38 |
| 37 // Set up the desired (output) format specified by the client. | 39 // Set up the desired (output) format specified by the client. |
| 38 format_.mSampleRate = params.sample_rate; | 40 format_.mSampleRate = params.sample_rate; |
| 39 format_.mFormatID = kAudioFormatLinearPCM; | 41 format_.mFormatID = kAudioFormatLinearPCM; |
| 40 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | 42 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | |
| 41 kLinearPCMFormatFlagIsSignedInteger; | 43 kLinearPCMFormatFlagIsSignedInteger; |
| 42 format_.mBitsPerChannel = params.bits_per_sample; | 44 format_.mBitsPerChannel = params.bits_per_sample; |
| 43 format_.mChannelsPerFrame = params.channels; | 45 format_.mChannelsPerFrame = params.channels; |
| 44 format_.mFramesPerPacket = 1; // uncompressed audio | 46 format_.mFramesPerPacket = 1; // uncompressed audio |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 129 0, // output element 0 | 131 0, // output element 0 |
| 130 &enableIO, // disable | 132 &enableIO, // disable |
| 131 sizeof(enableIO)); | 133 sizeof(enableIO)); |
| 132 if (result) { | 134 if (result) { |
| 133 HandleError(result); | 135 HandleError(result); |
| 134 return false; | 136 return false; |
| 135 } | 137 } |
| 136 | 138 |
| 137 // Set the current device of the AudioOuputUnit to default input device. | 139 // Set the current device of the AudioOuputUnit to default input device. |
| 138 | 140 |
| 139 AudioDeviceID input_device; | 141 // First, obtain the current input device selected by the user. |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
May I ask why you decided to use another API here?
no longer working on chromium
2011/10/21 12:21:46
Andrew pointed out that the AudioHardwareGetProper
| |
| 142 AudioObjectPropertyAddress default_intput_device_address = { | |
| 143 kAudioHardwarePropertyDefaultInputDevice, | |
| 144 kAudioObjectPropertyScopeGlobal, | |
| 145 kAudioObjectPropertyElementMaster | |
| 146 }; | |
| 147 AudioDeviceID input_device = kAudioObjectUnknown; | |
| 140 UInt32 size = sizeof(input_device); | 148 UInt32 size = sizeof(input_device); |
| 141 | 149 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, |
| 142 // First, obtain the current input device selected by the user. | 150 &default_intput_device_address, |
| 143 result = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, | 151 0, |
| 144 &size, | 152 0, |
| 145 &input_device); | 153 &size, |
| 154 &input_device); | |
| 146 if (result) { | 155 if (result) { |
| 147 HandleError(result); | 156 HandleError(result); |
| 148 return false; | 157 return false; |
| 149 } | 158 } |
| 150 | 159 |
| 160 // Store the input device id. | |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
Don't think this comment adds much.
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 161 input_device_id_ = input_device; | |
| 162 | |
| 151 // Next, set the audio device to be the Audio Unit's current device. | 163 // Next, set the audio device to be the Audio Unit's current device. |
| 152 // Note that, devices can only be set to the AUHAL after enabling IO. | 164 // Note that, devices can only be set to the AUHAL after enabling IO. |
| 153 result = AudioUnitSetProperty(audio_unit_, | 165 result = AudioUnitSetProperty(audio_unit_, |
| 154 kAudioOutputUnitProperty_CurrentDevice, | 166 kAudioOutputUnitProperty_CurrentDevice, |
| 155 kAudioUnitScope_Global, | 167 kAudioUnitScope_Global, |
| 156 0, | 168 0, |
| 157 &input_device, | 169 &input_device_id_, |
| 158 sizeof(input_device)); | 170 sizeof(input_device)); |
| 159 if (result) { | 171 if (result) { |
| 160 HandleError(result); | 172 HandleError(result); |
| 161 return false; | 173 return false; |
| 162 } | 174 } |
| 163 | 175 |
| 164 // Register the input procedure for the AUHAL. | 176 // Register the input procedure for the AUHAL. |
| 165 // This procedure will be called when the AUHAL has received new data | 177 // This procedure will be called when the AUHAL has received new data |
| 166 // from the input device. | 178 // from the input device. |
| 167 AURenderCallbackStruct callback; | 179 AURenderCallbackStruct callback; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 205 } | 217 } |
| 206 | 218 |
| 207 // Finally, initialize the audio unit and ensure that it is ready to render. | 219 // Finally, initialize the audio unit and ensure that it is ready to render. |
| 208 // Allocates memory according to the maximum number of audio frames | 220 // Allocates memory according to the maximum number of audio frames |
| 209 // it can produce in response to a single render call. | 221 // it can produce in response to a single render call. |
| 210 result = AudioUnitInitialize(audio_unit_); | 222 result = AudioUnitInitialize(audio_unit_); |
| 211 if (result) { | 223 if (result) { |
| 212 HandleError(result); | 224 HandleError(result); |
| 213 return false; | 225 return false; |
| 214 } | 226 } |
| 227 | |
| 228 hardware_latency_frames_ = GetHardwareLatency(); | |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
But a comment here could emphasize that parts of t
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 229 | |
| 215 return true; | 230 return true; |
| 216 } | 231 } |
| 217 | 232 |
| 218 void AUAudioInputStream::Start(AudioInputCallback* callback) { | 233 void AUAudioInputStream::Start(AudioInputCallback* callback) { |
| 219 DCHECK(callback); | 234 DCHECK(callback); |
| 220 if (started_) | 235 if (started_) |
| 221 return; | 236 return; |
| 222 sink_ = callback; | 237 sink_ = callback; |
| 223 OSStatus result = AudioOutputUnitStart(audio_unit_); | 238 OSStatus result = AudioOutputUnitStart(audio_unit_); |
| 224 if (result == noErr) { | 239 if (result == noErr) { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 282 flags, | 297 flags, |
| 283 time_stamp, | 298 time_stamp, |
| 284 bus_number, | 299 bus_number, |
| 285 number_of_frames, | 300 number_of_frames, |
| 286 audio_input->audio_buffer_list()); | 301 audio_input->audio_buffer_list()); |
| 287 if (result) | 302 if (result) |
| 288 return result; | 303 return result; |
| 289 | 304 |
| 290 // Deliver recorded data to the consumer as a callback. | 305 // Deliver recorded data to the consumer as a callback. |
| 291 return audio_input->Provide(number_of_frames, | 306 return audio_input->Provide(number_of_frames, |
| 292 audio_input->audio_buffer_list()); | 307 audio_input->audio_buffer_list(), |
| 308 time_stamp); | |
| 293 } | 309 } |
| 294 | 310 |
| 295 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, | 311 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, |
| 296 AudioBufferList* io_data) { | 312 AudioBufferList* io_data, |
| 313 const AudioTimeStamp* time_stamp) { | |
| 314 // Update the capture latency. | |
| 315 double capture_latency_frames = GetCaptureLatency(time_stamp); | |
| 316 | |
| 297 AudioBuffer& buffer = io_data->mBuffers[0]; | 317 AudioBuffer& buffer = io_data->mBuffers[0]; |
| 298 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); | 318 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); |
| 319 uint32 capture_delay_bytes = static_cast<uint32> | |
| 320 (capture_latency_frames * format_.mBytesPerFrame + 0.5); | |
| 299 DCHECK(audio_data); | 321 DCHECK(audio_data); |
| 300 if (!audio_data) | 322 if (!audio_data) |
| 301 return kAudioUnitErr_InvalidElement; | 323 return kAudioUnitErr_InvalidElement; |
| 302 | 324 |
| 303 // TODO(henrika): improve delay estimation. Using buffer size for now. | 325 sink_->OnData(this, audio_data, buffer.mDataByteSize, capture_delay_bytes); |
| 304 sink_->OnData(this, audio_data, buffer.mDataByteSize, buffer.mDataByteSize); | |
| 305 | 326 |
| 306 return noErr; | 327 return noErr; |
| 307 } | 328 } |
| 308 | 329 |
| 309 double AUAudioInputStream::HardwareSampleRate() { | 330 double AUAudioInputStream::HardwareSampleRate() { |
| 310 // Determine the default input device's sample-rate. | 331 // Determine the default input device's sample-rate. |
| 311 AudioDeviceID device_id = kAudioDeviceUnknown; | 332 AudioDeviceID device_id = kAudioObjectUnknown; |
| 312 UInt32 info_size = sizeof(device_id); | 333 UInt32 info_size = sizeof(device_id); |
| 313 | 334 |
| 314 AudioObjectPropertyAddress default_input_device_address = { | 335 AudioObjectPropertyAddress default_input_device_address = { |
| 315 kAudioHardwarePropertyDefaultInputDevice, | 336 kAudioHardwarePropertyDefaultInputDevice, |
| 316 kAudioObjectPropertyScopeGlobal, | 337 kAudioObjectPropertyScopeGlobal, |
| 317 kAudioObjectPropertyElementMaster | 338 kAudioObjectPropertyElementMaster |
| 318 }; | 339 }; |
| 319 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, | 340 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, |
| 320 &default_input_device_address, | 341 &default_input_device_address, |
| 321 0, | 342 0, |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 340 0, | 361 0, |
| 341 &info_size, | 362 &info_size, |
| 342 &nominal_sample_rate); | 363 &nominal_sample_rate); |
| 343 DCHECK_EQ(result, 0); | 364 DCHECK_EQ(result, 0); |
| 344 if (result) | 365 if (result) |
| 345 return 0.0; | 366 return 0.0; |
| 346 | 367 |
| 347 return nominal_sample_rate; | 368 return nominal_sample_rate; |
| 348 } | 369 } |
| 349 | 370 |
| 371 double AUAudioInputStream::GetHardwareLatency() { | |
| 372 if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) | |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
Is this a valid state? Guess it is since this part
no longer working on chromium
2011/10/21 12:21:46
It is safer to add a check here, since this functi
| |
| 373 return 0.0; | |
| 374 | |
| 375 // Get audio unit latency. | |
| 376 Float64 audio_unit_latency_sec = 0.0; | |
| 377 UInt32 size = sizeof(audio_unit_latency_sec); | |
| 378 OSStatus result = AudioUnitGetProperty(audio_unit_, | |
| 379 kAudioUnitProperty_Latency, | |
| 380 kAudioUnitScope_Global, | |
| 381 0, | |
| 382 &audio_unit_latency_sec, | |
| 383 &size); | |
| 384 if (result) { | |
| 385 DLOG(WARNING) << "GetHardwareLatency: Could not get audio unit latency."; | |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
Don't need GetHardwareLatency:, the log message is
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 386 } | |
| 387 | |
| 388 // Get audio device latency. | |
| 389 UInt32 device_latency_frames = 0; | |
| 390 size = sizeof(device_latency_frames); | |
| 391 result = AudioDeviceGetProperty(input_device_id_, 0, | |
| 392 true, | |
| 393 kAudioDevicePropertyLatency, | |
| 394 &size, | |
| 395 &device_latency_frames); | |
| 396 if (result) { | |
| 397 DLOG(WARNING) << "GetHardwareLatency: Could not get device latency."; | |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
ditto
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 398 } | |
| 399 | |
| 400 // Get the stream latency. | |
| 401 UInt32 stream_latency_frames = 0; | |
| 402 size = 0; | |
| 403 result = AudioDeviceGetPropertyInfo(input_device_id_, | |
| 404 0, | |
| 405 true, | |
| 406 kAudioDevicePropertyStreams, | |
| 407 &size, | |
| 408 NULL); | |
| 409 if (!result) { | |
| 410 scoped_ptr_malloc<AudioStreamID> | |
| 411 streams(reinterpret_cast<AudioStreamID*>(malloc(size))); | |
| 412 AudioStreamID* stream_ids = streams.get(); | |
| 413 result = AudioDeviceGetProperty(input_device_id_, | |
| 414 0, | |
| 415 true, | |
| 416 kAudioDevicePropertyStreams, | |
| 417 &size, | |
| 418 stream_ids); | |
| 419 if (!result) | |
| 420 result = AudioStreamGetProperty(stream_ids[0], | |
| 421 0, | |
| 422 kAudioStreamPropertyLatency, | |
| 423 &size, | |
| 424 &stream_latency_frames); | |
| 425 } | |
| 426 // Logs the warning if it fails to get the stream latency. | |
|
scherkus (not reviewing)
2011/10/21 00:20:58
nit: comment adds no value -- remove
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 427 if (result) { | |
| 428 DLOG(WARNING) << "GetHardwareLatency: Could not get stream latency."; | |
| 429 } | |
| 430 | |
| 431 // Store the hardware latency value in frames. | |
|
scherkus (not reviewing)
2011/10/21 00:20:58
ditto
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 432 return static_cast<double>(audio_unit_latency_sec * | |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
I would write it as ((x*a) + b + c)
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 433 format_.mSampleRate + device_latency_frames + stream_latency_frames); | |
| 434 } | |
| 435 | |
| 436 double AUAudioInputStream::GetCaptureLatency( | |
| 437 const AudioTimeStamp* input_time_stamp) { | |
| 438 // Get the delay between now and when the data was reaching the hardware. | |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
This comment is not clear. See my proposal in the
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 439 UInt64 input_time_ns = AudioConvertHostTimeToNanos( | |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
capture_time_ns
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 440 input_time_stamp->mHostTime); | |
| 441 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); | |
| 442 double delay_frames = static_cast<double> | |
| 443 (1e-9 * (now_ns - input_time_ns) * format_.mSampleRate); | |
| 444 | |
| 445 return (delay_frames + hardware_latency_frames_); | |
|
henrika (OOO until Aug 14)
2011/10/21 07:27:23
Comment about sum of fixed parts and dynamic part
no longer working on chromium
2011/10/21 12:21:46
Done.
| |
| 446 } | |
| 447 | |
| 350 void AUAudioInputStream::HandleError(OSStatus err) { | 448 void AUAudioInputStream::HandleError(OSStatus err) { |
| 351 NOTREACHED() << "error code: " << err; | 449 NOTREACHED() << "error code: " << err; |
| 352 if (sink_) | 450 if (sink_) |
| 353 sink_->OnError(this, static_cast<int>(err)); | 451 sink_->OnError(this, static_cast<int>(err)); |
| 354 } | 452 } |
| OLD | NEW |