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

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: '' 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_(kAudioDeviceUnknown),
35 started_(false),
36 hardware_latency_ms_(0),
37 capture_latency_ms_(0) {
35 DCHECK(manager_); 38 DCHECK(manager_);
36 39
37 // Set up the desired (output) format specified by the client. 40 // Set up the desired (output) format specified by the client.
38 format_.mSampleRate = params.sample_rate; 41 format_.mSampleRate = params.sample_rate;
39 format_.mFormatID = kAudioFormatLinearPCM; 42 format_.mFormatID = kAudioFormatLinearPCM;
40 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | 43 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
41 kLinearPCMFormatFlagIsSignedInteger; 44 kLinearPCMFormatFlagIsSignedInteger;
42 format_.mBitsPerChannel = params.bits_per_sample; 45 format_.mBitsPerChannel = params.bits_per_sample;
43 format_.mChannelsPerFrame = params.channels; 46 format_.mChannelsPerFrame = params.channels;
44 format_.mFramesPerPacket = 1; // uncompressed audio 47 format_.mFramesPerPacket = 1; // uncompressed audio
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 0, // output element 0 132 0, // output element 0
130 &enableIO, // disable 133 &enableIO, // disable
131 sizeof(enableIO)); 134 sizeof(enableIO));
132 if (result) { 135 if (result) {
133 HandleError(result); 136 HandleError(result);
134 return false; 137 return false;
135 } 138 }
136 139
137 // Set the current device of the AudioOuputUnit to default input device. 140 // Set the current device of the AudioOuputUnit to default input device.
138 141
139 AudioDeviceID input_device; 142 UInt32 size = sizeof(input_device_id_);
henrika (OOO until Aug 14) 2011/10/12 12:10:11 I would use a local here and only assign if we are
no longer working on chromium 2011/10/12 15:28:47 Done.
140 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_id_);
146 if (result) { 148 if (result) {
147 HandleError(result); 149 HandleError(result);
148 return false; 150 return false;
149 } 151 }
150 152
151 // Next, set the audio device to be the Audio Unit's current device. 153 // 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. 154 // Note that, devices can only be set to the AUHAL after enabling IO.
153 result = AudioUnitSetProperty(audio_unit_, 155 result = AudioUnitSetProperty(audio_unit_,
154 kAudioOutputUnitProperty_CurrentDevice, 156 kAudioOutputUnitProperty_CurrentDevice,
155 kAudioUnitScope_Global, 157 kAudioUnitScope_Global,
156 0, 158 0,
157 &input_device, 159 &input_device_id_,
158 sizeof(input_device)); 160 sizeof(input_device_id_));
159 if (result) { 161 if (result) {
160 HandleError(result); 162 HandleError(result);
161 return false; 163 return false;
162 } 164 }
163 165
164 // Register the input procedure for the AUHAL. 166 // Register the input procedure for the AUHAL.
165 // This procedure will be called when the AUHAL has received new data 167 // This procedure will be called when the AUHAL has received new data
166 // from the input device. 168 // from the input device.
167 AURenderCallbackStruct callback; 169 AURenderCallbackStruct callback;
168 callback.inputProc = InputProc; 170 callback.inputProc = InputProc;
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 } 207 }
206 208
207 // Finally, initialize the audio unit and ensure that it is ready to render. 209 // Finally, initialize the audio unit and ensure that it is ready to render.
208 // Allocates memory according to the maximum number of audio frames 210 // Allocates memory according to the maximum number of audio frames
209 // it can produce in response to a single render call. 211 // it can produce in response to a single render call.
210 result = AudioUnitInitialize(audio_unit_); 212 result = AudioUnitInitialize(audio_unit_);
211 if (result) { 213 if (result) {
212 HandleError(result); 214 HandleError(result);
213 return false; 215 return false;
214 } 216 }
217
218 // Update the capture device hardware latency.
henrika (OOO until Aug 14) 2011/10/12 12:10:11 Can be a static method instead.
no longer working on chromium 2011/10/12 15:28:47 Can't make it since the function needs some member
219 UpdateHardwareLatency();
220
215 return true; 221 return true;
216 } 222 }
217 223
218 void AUAudioInputStream::Start(AudioInputCallback* callback) { 224 void AUAudioInputStream::Start(AudioInputCallback* callback) {
219 DCHECK(callback); 225 DCHECK(callback);
220 if (started_) 226 if (started_)
221 return; 227 return;
222 sink_ = callback; 228 sink_ = callback;
223 OSStatus result = AudioOutputUnitStart(audio_unit_); 229 OSStatus result = AudioOutputUnitStart(audio_unit_);
224 if (result == noErr) { 230 if (result == noErr) {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
282 flags, 288 flags,
283 time_stamp, 289 time_stamp,
284 bus_number, 290 bus_number,
285 number_of_frames, 291 number_of_frames,
286 audio_input->audio_buffer_list()); 292 audio_input->audio_buffer_list());
287 if (result) 293 if (result)
288 return result; 294 return result;
289 295
290 // Deliver recorded data to the consumer as a callback. 296 // Deliver recorded data to the consumer as a callback.
291 return audio_input->Provide(number_of_frames, 297 return audio_input->Provide(number_of_frames,
292 audio_input->audio_buffer_list()); 298 audio_input->audio_buffer_list(),
299 time_stamp);
293 } 300 }
294 301
295 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, 302 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
296 AudioBufferList* io_data) { 303 AudioBufferList* io_data,
304 const AudioTimeStamp* time_stamp) {
305 // Update the capture latency.
306 UpdateCaptureLatency(time_stamp);
henrika (OOO until Aug 14) 2011/10/12 12:10:11 Have you checked if we need to call it each callba
no longer working on chromium 2011/10/12 15:28:47 The value should be quite stable in the normal cas
307
297 AudioBuffer& buffer = io_data->mBuffers[0]; 308 AudioBuffer& buffer = io_data->mBuffers[0];
298 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); 309 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
310 uint32 capture_delay_bytes = (1.0e-3 * capture_latency_ms_ *
henrika (OOO until Aug 14) 2011/10/12 12:10:11 I would divide by 1000, and clean up casting here.
no longer working on chromium 2011/10/12 15:28:47 Done.
311 format_.mSampleRate * format_.mBytesPerFrame);
299 DCHECK(audio_data); 312 DCHECK(audio_data);
300 if (!audio_data) 313 if (!audio_data)
301 return kAudioUnitErr_InvalidElement; 314 return kAudioUnitErr_InvalidElement;
302 315
303 // TODO(henrika): improve delay estimation. Using buffer size for now. 316 sink_->OnData(this, audio_data, buffer.mDataByteSize, capture_delay_bytes);
304 sink_->OnData(this, audio_data, buffer.mDataByteSize, buffer.mDataByteSize);
305 317
306 return noErr; 318 return noErr;
307 } 319 }
308 320
309 double AUAudioInputStream::HardwareSampleRate() { 321 double AUAudioInputStream::HardwareSampleRate() {
310 // Determine the default input device's sample-rate. 322 // Determine the default input device's sample-rate.
311 AudioDeviceID device_id = kAudioDeviceUnknown; 323 AudioDeviceID device_id = kAudioDeviceUnknown;
312 UInt32 info_size = sizeof(device_id); 324 UInt32 info_size = sizeof(device_id);
313 325
314 AudioObjectPropertyAddress default_input_device_address = { 326 AudioObjectPropertyAddress default_input_device_address = {
(...skipping 25 matching lines...) Expand all
340 0, 352 0,
341 &info_size, 353 &info_size,
342 &nominal_sample_rate); 354 &nominal_sample_rate);
343 DCHECK_EQ(result, 0); 355 DCHECK_EQ(result, 0);
344 if (result) 356 if (result)
345 return 0.0; 357 return 0.0;
346 358
347 return nominal_sample_rate; 359 return nominal_sample_rate;
348 } 360 }
349 361
362 void AUAudioInputStream::UpdateHardwareLatency() {
363 // Get audio unit latency.
364 Float64 audio_unit_latency_s = 0;
henrika (OOO until Aug 14) 2011/10/12 12:10:11 sec and 0.0.
no longer working on chromium 2011/10/12 15:28:47 Done.
365 UInt32 size = sizeof(audio_unit_latency_s);
366 OSStatus result = AudioUnitGetProperty(
henrika (OOO until Aug 14) 2011/10/12 12:10:11 Use same style as in rest of the code.
no longer working on chromium 2011/10/12 15:28:47 Done.
367 audio_unit_, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
368 0, &audio_unit_latency_s, &size);
369 if (result) {
370 DLOG(WARNING) << "GetHardwareLatency: Could not get audio unit latency.";
371 audio_unit_latency_s = 0;
henrika (OOO until Aug 14) 2011/10/12 12:10:11 not needed
no longer working on chromium 2011/10/12 15:28:47 Done.
372 }
373
374 // Get audio device latency.
375 UInt32 device_latency_frames = 0;
376 size = sizeof(device_latency_frames);
377 result = AudioDeviceGetProperty(input_device_id_, 0, true,
henrika (OOO until Aug 14) 2011/10/12 12:10:11 ditto
no longer working on chromium 2011/10/12 15:28:47 Done.
378 kAudioDevicePropertyLatency, &size,
379 &device_latency_frames);
380 if (result) {
381 DLOG(WARNING) << "GetHardwareLatency: Could not get device latency.";
382 device_latency_frames = 0;
henrika (OOO until Aug 14) 2011/10/12 12:10:11 ditto
no longer working on chromium 2011/10/12 15:28:47 Done.
383 }
384
385 // Get the stream latency.
386 UInt32 stream_latency_frames = 0;
387 size = 0;
388 result = AudioDeviceGetPropertyInfo(input_device_id_, 0, true,
henrika (OOO until Aug 14) 2011/10/12 12:10:11 ditto
no longer working on chromium 2011/10/12 15:28:47 Done.
389 kAudioDevicePropertyStreams, &size, NULL);
390 if (!result) {
391 scoped_ptr_malloc<AudioStreamID>
392 streams(reinterpret_cast<AudioStreamID*>(malloc(size)));
393 AudioStreamID* stream_ids = streams.get();
394 result = AudioDeviceGetProperty(
henrika (OOO until Aug 14) 2011/10/12 12:10:11 ditto
no longer working on chromium 2011/10/12 15:28:47 Done.
395 input_device_id_, 0, true,
396 kAudioDevicePropertyStreams, &size, stream_ids);
397 if (result) {
398 DLOG(WARNING) << "UpdateHardwareLatency: Could not get stream id.";
399 return;
henrika (OOO until Aug 14) 2011/10/12 12:10:11 Wrong!
no longer working on chromium 2011/10/12 15:28:47 Done.
400 }
401
402 result = AudioStreamGetProperty(
403 stream_ids[0], 0, kAudioStreamPropertyLatency,
henrika (OOO until Aug 14) 2011/10/12 12:10:11 ditto
no longer working on chromium 2011/10/12 15:28:47 Done.
404 &size, &stream_latency_frames);
405 if (result) {
406 DLOG(WARNING) << "UpdateHardwareLatency: Could not get stream latency.";
407 stream_latency_frames = 0;
408 }
409 } else {
410 DLOG(WARNING) << "UpdateHardwareLatency: Could not get stream id.";
411 }
412
413 hardware_latency_ms_ = static_cast<uint32>(1.0e3 * audio_unit_latency_s +
henrika (OOO until Aug 14) 2011/10/12 12:10:11 Are you sure?
no longer working on chromium 2011/10/12 15:28:47 Done.
414 (1.0e3 * (device_latency_frames + stream_latency_frames)
415 / format_.mSampleRate) + 0.5);
416 }
417
418 void AUAudioInputStream::UpdateCaptureLatency(
419 const AudioTimeStamp* input_time_stamp) {
420 // Get the delay between now and when the data was hitting the hardware.
henrika (OOO until Aug 14) 2011/10/12 12:10:11 aooch ;-)
no longer working on chromium 2011/10/12 15:28:47 Done.
421 UInt64 input_time_ns = AudioConvertHostTimeToNanos(
422 input_time_stamp->mHostTime);
423 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
424 uint32 delay_ms = static_cast<uint32>(1e-6 * (now_ns - input_time_ns) + 0.5);
henrika (OOO until Aug 14) 2011/10/12 12:10:11 Are you sure?
no longer working on chromium 2011/10/12 15:28:47 I would like to keep it, since it is a more readab
425
426 capture_latency_ms_ = delay_ms + hardware_latency_ms_;
henrika (OOO until Aug 14) 2011/10/12 12:10:11 I would add a DCHECK here just in case (santity?)
no longer working on chromium 2011/10/12 15:28:47 I need to think a bit more here. For other platfor
427 }
428
350 void AUAudioInputStream::HandleError(OSStatus err) { 429 void AUAudioInputStream::HandleError(OSStatus err) {
351 NOTREACHED() << "error code: " << err; 430 NOTREACHED() << "error code: " << err;
352 if (sink_) 431 if (sink_)
353 sink_->OnError(this, static_cast<int>(err)); 432 sink_->OnError(this, static_cast<int>(err));
354 } 433 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698