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_(kAudioDeviceUnknown), |
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 AudioDeviceID input_device = kAudioDeviceUnknown; |
140 UInt32 size = sizeof(input_device); | 142 UInt32 size = sizeof(input_device); |
141 | 143 |
142 // First, obtain the current input device selected by the user. | 144 // First, obtain the current input device selected by the user. |
143 result = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, | 145 result = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, |
144 &size, | 146 &size, |
145 &input_device); | 147 &input_device); |
146 if (result) { | 148 if (result) { |
147 HandleError(result); | 149 HandleError(result); |
148 return false; | 150 return false; |
149 } | 151 } |
150 | 152 |
153 // Store the input device id. | |
154 input_device_id_ = input_device; | |
155 | |
151 // Next, set the audio device to be the Audio Unit's current device. | 156 // 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. | 157 // Note that, devices can only be set to the AUHAL after enabling IO. |
153 result = AudioUnitSetProperty(audio_unit_, | 158 result = AudioUnitSetProperty(audio_unit_, |
154 kAudioOutputUnitProperty_CurrentDevice, | 159 kAudioOutputUnitProperty_CurrentDevice, |
155 kAudioUnitScope_Global, | 160 kAudioUnitScope_Global, |
156 0, | 161 0, |
157 &input_device, | 162 &input_device_id_, |
158 sizeof(input_device)); | 163 sizeof(input_device)); |
159 if (result) { | 164 if (result) { |
160 HandleError(result); | 165 HandleError(result); |
161 return false; | 166 return false; |
162 } | 167 } |
163 | 168 |
164 // Register the input procedure for the AUHAL. | 169 // Register the input procedure for the AUHAL. |
165 // This procedure will be called when the AUHAL has received new data | 170 // This procedure will be called when the AUHAL has received new data |
166 // from the input device. | 171 // from the input device. |
167 AURenderCallbackStruct callback; | 172 AURenderCallbackStruct callback; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
205 } | 210 } |
206 | 211 |
207 // Finally, initialize the audio unit and ensure that it is ready to render. | 212 // Finally, initialize the audio unit and ensure that it is ready to render. |
208 // Allocates memory according to the maximum number of audio frames | 213 // Allocates memory according to the maximum number of audio frames |
209 // it can produce in response to a single render call. | 214 // it can produce in response to a single render call. |
210 result = AudioUnitInitialize(audio_unit_); | 215 result = AudioUnitInitialize(audio_unit_); |
211 if (result) { | 216 if (result) { |
212 HandleError(result); | 217 HandleError(result); |
213 return false; | 218 return false; |
214 } | 219 } |
220 | |
221 // Gets the capture device hardware latency and stores the value. | |
scherkus (not reviewing)
2011/10/19 18:23:57
comment isn't needed
no longer working on chromium
2011/10/20 11:25:51
Done.
| |
222 hardware_latency_frames_ = GetHardwareLatency(); | |
223 | |
215 return true; | 224 return true; |
216 } | 225 } |
217 | 226 |
218 void AUAudioInputStream::Start(AudioInputCallback* callback) { | 227 void AUAudioInputStream::Start(AudioInputCallback* callback) { |
219 DCHECK(callback); | 228 DCHECK(callback); |
220 if (started_) | 229 if (started_) |
221 return; | 230 return; |
222 sink_ = callback; | 231 sink_ = callback; |
223 OSStatus result = AudioOutputUnitStart(audio_unit_); | 232 OSStatus result = AudioOutputUnitStart(audio_unit_); |
224 if (result == noErr) { | 233 if (result == noErr) { |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
282 flags, | 291 flags, |
283 time_stamp, | 292 time_stamp, |
284 bus_number, | 293 bus_number, |
285 number_of_frames, | 294 number_of_frames, |
286 audio_input->audio_buffer_list()); | 295 audio_input->audio_buffer_list()); |
287 if (result) | 296 if (result) |
288 return result; | 297 return result; |
289 | 298 |
290 // Deliver recorded data to the consumer as a callback. | 299 // Deliver recorded data to the consumer as a callback. |
291 return audio_input->Provide(number_of_frames, | 300 return audio_input->Provide(number_of_frames, |
292 audio_input->audio_buffer_list()); | 301 audio_input->audio_buffer_list(), |
302 time_stamp); | |
293 } | 303 } |
294 | 304 |
295 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, | 305 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, |
296 AudioBufferList* io_data) { | 306 AudioBufferList* io_data, |
307 const AudioTimeStamp* time_stamp) { | |
308 // Update the capture latency. | |
309 double capture_latency_frames = GetCaptureLatency(time_stamp); | |
310 | |
297 AudioBuffer& buffer = io_data->mBuffers[0]; | 311 AudioBuffer& buffer = io_data->mBuffers[0]; |
298 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); | 312 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); |
313 uint32 capture_delay_bytes = static_cast<uint32> | |
314 (capture_latency_frames * format_.mBytesPerFrame + 0.5); | |
299 DCHECK(audio_data); | 315 DCHECK(audio_data); |
300 if (!audio_data) | 316 if (!audio_data) |
301 return kAudioUnitErr_InvalidElement; | 317 return kAudioUnitErr_InvalidElement; |
302 | 318 |
303 // TODO(henrika): improve delay estimation. Using buffer size for now. | 319 sink_->OnData(this, audio_data, buffer.mDataByteSize, capture_delay_bytes); |
304 sink_->OnData(this, audio_data, buffer.mDataByteSize, buffer.mDataByteSize); | |
305 | 320 |
306 return noErr; | 321 return noErr; |
307 } | 322 } |
308 | 323 |
309 double AUAudioInputStream::HardwareSampleRate() { | 324 double AUAudioInputStream::HardwareSampleRate() { |
310 // Determine the default input device's sample-rate. | 325 // Determine the default input device's sample-rate. |
311 AudioDeviceID device_id = kAudioDeviceUnknown; | 326 AudioDeviceID device_id = kAudioDeviceUnknown; |
312 UInt32 info_size = sizeof(device_id); | 327 UInt32 info_size = sizeof(device_id); |
313 | 328 |
314 AudioObjectPropertyAddress default_input_device_address = { | 329 AudioObjectPropertyAddress default_input_device_address = { |
(...skipping 25 matching lines...) Expand all Loading... | |
340 0, | 355 0, |
341 &info_size, | 356 &info_size, |
342 &nominal_sample_rate); | 357 &nominal_sample_rate); |
343 DCHECK_EQ(result, 0); | 358 DCHECK_EQ(result, 0); |
344 if (result) | 359 if (result) |
345 return 0.0; | 360 return 0.0; |
346 | 361 |
347 return nominal_sample_rate; | 362 return nominal_sample_rate; |
348 } | 363 } |
349 | 364 |
365 double AUAudioInputStream::GetHardwareLatency() { | |
366 // Get audio unit latency. | |
367 Float64 audio_unit_latency_sec = 0.0; | |
368 UInt32 size = sizeof(audio_unit_latency_sec); | |
369 OSStatus result = AudioUnitGetProperty(audio_unit_, | |
370 kAudioUnitProperty_Latency, | |
371 kAudioUnitScope_Global, | |
372 0, | |
373 &audio_unit_latency_sec, | |
374 &size); | |
375 if (result) | |
scherkus (not reviewing)
2011/10/19 18:23:57
nit: add {} when we have a macro as the only state
no longer working on chromium
2011/10/20 11:25:51
Done.
| |
376 DLOG(WARNING) << "GetHardwareLatency: Could not get audio unit latency."; | |
377 | |
378 // Get audio device latency. | |
379 UInt32 device_latency_frames = 0; | |
380 size = sizeof(device_latency_frames); | |
381 result = AudioDeviceGetProperty(input_device_id_, 0, | |
382 true, | |
383 kAudioDevicePropertyLatency, | |
384 &size, | |
385 &device_latency_frames); | |
386 if (result) | |
scherkus (not reviewing)
2011/10/19 18:23:57
ditto + for rest of result checks
no longer working on chromium
2011/10/20 11:25:51
Done.
| |
387 DLOG(WARNING) << "GetHardwareLatency: Could not get device latency."; | |
388 | |
389 // Get the stream latency. | |
390 UInt32 stream_latency_frames = 0; | |
391 size = 0; | |
392 result = AudioDeviceGetPropertyInfo(input_device_id_, | |
393 0, | |
394 true, | |
395 kAudioDevicePropertyStreams, | |
396 &size, | |
397 NULL); | |
398 if (!result) { | |
399 scoped_ptr_malloc<AudioStreamID> | |
400 streams(reinterpret_cast<AudioStreamID*>(malloc(size))); | |
401 AudioStreamID* stream_ids = streams.get(); | |
402 result = AudioDeviceGetProperty(input_device_id_, | |
403 0, | |
404 true, | |
405 kAudioDevicePropertyStreams, | |
406 &size, | |
407 stream_ids); | |
408 if (!result) | |
409 result = AudioStreamGetProperty(stream_ids[0], | |
410 0, | |
411 kAudioStreamPropertyLatency, | |
412 &size, | |
413 &stream_latency_frames); | |
414 } | |
415 // Logs the warning if it fails to get the stream latency. | |
416 if (result) | |
417 DLOG(WARNING) << "GetHardwareLatency: Could not get stream latency."; | |
418 | |
419 // Store the hardware latency value in frames. | |
420 return static_cast<double>(audio_unit_latency_sec * | |
421 format_.mSampleRate + device_latency_frames + stream_latency_frames); | |
422 } | |
423 | |
424 double AUAudioInputStream::GetCaptureLatency( | |
425 const AudioTimeStamp* input_time_stamp) { | |
426 // Get the delay between now and when the data was reaching the hardware. | |
427 UInt64 input_time_ns = AudioConvertHostTimeToNanos( | |
428 input_time_stamp->mHostTime); | |
429 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); | |
430 double delay_frames = static_cast<double> | |
431 (1e-9 * (now_ns - input_time_ns) * format_.mSampleRate); | |
432 | |
433 return (delay_frames + hardware_latency_frames_); | |
434 } | |
435 | |
350 void AUAudioInputStream::HandleError(OSStatus err) { | 436 void AUAudioInputStream::HandleError(OSStatus err) { |
351 NOTREACHED() << "error code: " << err; | 437 NOTREACHED() << "error code: " << err; |
352 if (sink_) | 438 if (sink_) |
353 sink_->OnError(this, static_cast<int>(err)); | 439 sink_->OnError(this, static_cast<int>(err)); |
354 } | 440 } |
OLD | NEW |