Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Side by Side Diff: media/audio/mac/audio_low_latency_input_mac.cc

Issue 8234009: Adding input and output delay estimation for mac. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: update Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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.
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 input_device_id_ = input_device;
161
151 // Next, set the audio device to be the Audio Unit's current device. 162 // 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. 163 // Note that, devices can only be set to the AUHAL after enabling IO.
153 result = AudioUnitSetProperty(audio_unit_, 164 result = AudioUnitSetProperty(audio_unit_,
154 kAudioOutputUnitProperty_CurrentDevice, 165 kAudioOutputUnitProperty_CurrentDevice,
155 kAudioUnitScope_Global, 166 kAudioUnitScope_Global,
156 0, 167 0,
157 &input_device, 168 &input_device_id_,
158 sizeof(input_device)); 169 sizeof(input_device));
159 if (result) { 170 if (result) {
160 HandleError(result); 171 HandleError(result);
161 return false; 172 return false;
162 } 173 }
163 174
164 // Register the input procedure for the AUHAL. 175 // Register the input procedure for the AUHAL.
165 // This procedure will be called when the AUHAL has received new data 176 // This procedure will be called when the AUHAL has received new data
166 // from the input device. 177 // from the input device.
167 AURenderCallbackStruct callback; 178 AURenderCallbackStruct callback;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 } 216 }
206 217
207 // Finally, initialize the audio unit and ensure that it is ready to render. 218 // Finally, initialize the audio unit and ensure that it is ready to render.
208 // Allocates memory according to the maximum number of audio frames 219 // Allocates memory according to the maximum number of audio frames
209 // it can produce in response to a single render call. 220 // it can produce in response to a single render call.
210 result = AudioUnitInitialize(audio_unit_); 221 result = AudioUnitInitialize(audio_unit_);
211 if (result) { 222 if (result) {
212 HandleError(result); 223 HandleError(result);
213 return false; 224 return false;
214 } 225 }
226
227 // The hardware latency is fixed and will not change during the call.
228 hardware_latency_frames_ = GetHardwareLatency();
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
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);
Chris Rogers 2011/10/26 19:03:53 Don't we want to convert "capture_latency_frames"
no longer working on chromium 2011/10/26 22:07:23 Good question. I did the rounding after the conver
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
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) {
373 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
374 return 0.0;
375 }
376
377 // Get audio unit latency.
378 Float64 audio_unit_latency_sec = 0.0;
379 UInt32 size = sizeof(audio_unit_latency_sec);
380 OSStatus result = AudioUnitGetProperty(audio_unit_,
381 kAudioUnitProperty_Latency,
382 kAudioUnitScope_Global,
383 0,
384 &audio_unit_latency_sec,
385 &size);
386 DLOG_IF(WARNING, result != noErr) << "Could not get audio unit latency.";
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 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
397
398 // Get the stream latency.
399 UInt32 stream_latency_frames = 0;
400 size = 0;
401 result = AudioDeviceGetPropertyInfo(input_device_id_,
402 0,
403 true,
404 kAudioDevicePropertyStreams,
405 &size,
406 NULL);
407 if (!result) {
408 scoped_ptr_malloc<AudioStreamID>
409 streams(reinterpret_cast<AudioStreamID*>(malloc(size)));
410 AudioStreamID* stream_ids = streams.get();
411 result = AudioDeviceGetProperty(input_device_id_,
412 0,
413 true,
414 kAudioDevicePropertyStreams,
415 &size,
416 stream_ids);
417 if (!result)
418 result = AudioStreamGetProperty(stream_ids[0],
419 0,
420 kAudioStreamPropertyLatency,
421 &size,
422 &stream_latency_frames);
423 }
424 DLOG_IF(WARNING, result != noErr) << "Could not get audio stream latency.";
425
426 return static_cast<double>((audio_unit_latency_sec *
427 format_.mSampleRate) + device_latency_frames + stream_latency_frames);
428 }
429
430 double AUAudioInputStream::GetCaptureLatency(
431 const AudioTimeStamp* input_time_stamp) {
432 // Get the delay between between the actual recording instant and the time
433 // when the data packet is provided as a callback
Chris Rogers 2011/10/26 19:03:53 nit: period at end of sentence
no longer working on chromium 2011/10/26 22:07:23 Done.
434 UInt64 capture_time_ns = AudioConvertHostTimeToNanos(
435 input_time_stamp->mHostTime);
436 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
437 double delay_frames = static_cast<double>
438 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate);
439
440 // Total latency is composed by the dynamic latency and the fixed
441 // hardware latency.
442 return (delay_frames + hardware_latency_frames_);
443 }
444
350 void AUAudioInputStream::HandleError(OSStatus err) { 445 void AUAudioInputStream::HandleError(OSStatus err) {
351 NOTREACHED() << "error code: " << err; 446 NOTREACHED() << "error code: " << err;
352 if (sink_) 447 if (sink_)
353 sink_->OnError(this, static_cast<int>(err)); 448 sink_->OnError(this, static_cast<int>(err));
354 } 449 }
OLDNEW
« no previous file with comments | « media/audio/mac/audio_low_latency_input_mac.h ('k') | media/audio/mac/audio_low_latency_input_mac_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698