OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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_auhal_mac.h" | 5 #include "media/audio/mac/audio_auhal_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/bind.h" | 10 #include "base/bind.h" |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
44 // Finally set the actual length. | 44 // Finally set the actual length. |
45 bus->set_frames(frames); | 45 bus->set_frames(frames); |
46 } | 46 } |
47 | 47 |
48 AUHALStream::AUHALStream( | 48 AUHALStream::AUHALStream( |
49 AudioManagerMac* manager, | 49 AudioManagerMac* manager, |
50 const AudioParameters& params, | 50 const AudioParameters& params, |
51 AudioDeviceID device) | 51 AudioDeviceID device) |
52 : manager_(manager), | 52 : manager_(manager), |
53 params_(params), | 53 params_(params), |
54 input_channels_(params_.input_channels()), | |
55 output_channels_(params_.channels()), | 54 output_channels_(params_.channels()), |
56 number_of_frames_(params_.frames_per_buffer()), | 55 number_of_frames_(params_.frames_per_buffer()), |
57 source_(NULL), | 56 source_(NULL), |
58 device_(device), | 57 device_(device), |
59 audio_unit_(0), | 58 audio_unit_(0), |
60 volume_(1), | 59 volume_(1), |
61 hardware_latency_frames_(0), | 60 hardware_latency_frames_(0), |
62 stopped_(false), | 61 stopped_(false), |
63 input_buffer_list_(NULL), | |
64 current_hardware_pending_bytes_(0) { | 62 current_hardware_pending_bytes_(0) { |
65 // We must have a manager. | 63 // We must have a manager. |
66 DCHECK(manager_); | 64 DCHECK(manager_); |
67 | 65 |
68 VLOG(1) << "AUHALStream::AUHALStream()"; | 66 VLOG(1) << "AUHALStream::AUHALStream()"; |
69 VLOG(1) << "Device: " << device; | 67 VLOG(1) << "Device: " << device; |
70 VLOG(1) << "Input channels: " << input_channels_; | |
71 VLOG(1) << "Output channels: " << output_channels_; | 68 VLOG(1) << "Output channels: " << output_channels_; |
72 VLOG(1) << "Sample rate: " << params_.sample_rate(); | 69 VLOG(1) << "Sample rate: " << params_.sample_rate(); |
73 VLOG(1) << "Buffer size: " << number_of_frames_; | 70 VLOG(1) << "Buffer size: " << number_of_frames_; |
74 } | 71 } |
75 | 72 |
76 AUHALStream::~AUHALStream() { | 73 AUHALStream::~AUHALStream() { |
77 } | 74 } |
78 | 75 |
79 bool AUHALStream::Open() { | 76 bool AUHALStream::Open() { |
80 // Get the total number of input and output channels that the | 77 // Get the total number of output channels that the |
81 // hardware supports. | 78 // hardware supports. |
82 int device_input_channels; | |
83 bool got_input_channels = AudioManagerMac::GetDeviceChannels( | |
84 device_, | |
85 kAudioDevicePropertyScopeInput, | |
86 &device_input_channels); | |
87 | |
88 int device_output_channels; | 79 int device_output_channels; |
89 bool got_output_channels = AudioManagerMac::GetDeviceChannels( | 80 bool got_output_channels = AudioManagerMac::GetDeviceChannels( |
90 device_, | 81 device_, |
91 kAudioDevicePropertyScopeOutput, | 82 kAudioDevicePropertyScopeOutput, |
92 &device_output_channels); | 83 &device_output_channels); |
93 | 84 |
94 // Sanity check the requested I/O channels. | 85 // Sanity check the requested output channels. |
95 if (!got_input_channels || | |
96 input_channels_ < 0 || input_channels_ > device_input_channels) { | |
97 LOG(ERROR) << "AudioDevice does not support requested input channels."; | |
98 return false; | |
99 } | |
100 | |
101 if (!got_output_channels || | 86 if (!got_output_channels || |
102 output_channels_ <= 0 || output_channels_ > device_output_channels) { | 87 output_channels_ <= 0 || output_channels_ > device_output_channels) { |
103 LOG(ERROR) << "AudioDevice does not support requested output channels."; | 88 LOG(ERROR) << "AudioDevice does not support requested output channels."; |
104 return false; | 89 return false; |
105 } | 90 } |
106 | 91 |
107 // The requested sample-rate must match the hardware sample-rate. | 92 // The requested sample-rate must match the hardware sample-rate. |
108 int sample_rate = AudioManagerMac::HardwareSampleRateForDevice(device_); | 93 int sample_rate = AudioManagerMac::HardwareSampleRateForDevice(device_); |
109 | 94 |
110 if (sample_rate != params_.sample_rate()) { | 95 if (sample_rate != params_.sample_rate()) { |
111 LOG(ERROR) << "Requested sample-rate: " << params_.sample_rate() | 96 LOG(ERROR) << "Requested sample-rate: " << params_.sample_rate() |
112 << " must match the hardware sample-rate: " << sample_rate; | 97 << " must match the hardware sample-rate: " << sample_rate; |
113 return false; | 98 return false; |
114 } | 99 } |
115 | 100 |
116 CreateIOBusses(); | 101 // The output bus will wrap the AudioBufferList given to us in |
102 // the Render() callback. | |
103 DCHECK_GT(output_channels_, 0); | |
104 output_bus_ = AudioBus::CreateWrapper(output_channels_); | |
117 | 105 |
118 bool configured = ConfigureAUHAL(); | 106 bool configured = ConfigureAUHAL(); |
119 if (configured) | 107 if (configured) |
120 hardware_latency_frames_ = GetHardwareLatency(); | 108 hardware_latency_frames_ = GetHardwareLatency(); |
121 | 109 |
122 return configured; | 110 return configured; |
123 } | 111 } |
124 | 112 |
125 void AUHALStream::Close() { | 113 void AUHALStream::Close() { |
126 if (input_buffer_list_) { | |
127 input_buffer_list_storage_.reset(); | |
128 input_buffer_list_ = NULL; | |
129 input_bus_.reset(NULL); | |
130 output_bus_.reset(NULL); | |
131 } | |
132 | |
133 if (audio_unit_) { | 114 if (audio_unit_) { |
134 OSStatus result = AudioUnitUninitialize(audio_unit_); | 115 OSStatus result = AudioUnitUninitialize(audio_unit_); |
135 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 116 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
136 << "AudioUnitUninitialize() failed."; | 117 << "AudioUnitUninitialize() failed."; |
137 result = AudioComponentInstanceDispose(audio_unit_); | 118 result = AudioComponentInstanceDispose(audio_unit_); |
138 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 119 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
139 << "AudioComponentInstanceDispose() failed."; | 120 << "AudioComponentInstanceDispose() failed."; |
140 } | 121 } |
141 | 122 |
142 // Inform the audio manager that we have been closed. This will cause our | 123 // Inform the audio manager that we have been closed. This will cause our |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
205 | 186 |
206 // Pulls on our provider to get rendered audio stream. | 187 // Pulls on our provider to get rendered audio stream. |
207 // Note to future hackers of this function: Do not add locks which can | 188 // Note to future hackers of this function: Do not add locks which can |
208 // be contended in the middle of stream processing here (starting and stopping | 189 // be contended in the middle of stream processing here (starting and stopping |
209 // the stream are ok) because this is running on a real-time thread. | 190 // the stream are ok) because this is running on a real-time thread. |
210 OSStatus AUHALStream::Render( | 191 OSStatus AUHALStream::Render( |
211 AudioUnitRenderActionFlags* flags, | 192 AudioUnitRenderActionFlags* flags, |
212 const AudioTimeStamp* output_time_stamp, | 193 const AudioTimeStamp* output_time_stamp, |
213 UInt32 bus_number, | 194 UInt32 bus_number, |
214 UInt32 number_of_frames, | 195 UInt32 number_of_frames, |
215 AudioBufferList* io_data) { | 196 AudioBufferList* data) { |
216 TRACE_EVENT0("audio", "AUHALStream::Render"); | 197 TRACE_EVENT0("audio", "AUHALStream::Render"); |
217 | 198 |
218 // If the stream parameters change for any reason, we need to insert a FIFO | 199 // If the stream parameters change for any reason, we need to insert a FIFO |
219 // since the OnMoreData() pipeline can't handle frame size changes. | 200 // since the OnMoreData() pipeline can't handle frame size changes. |
220 if (number_of_frames != number_of_frames_) { | 201 if (number_of_frames != number_of_frames_) { |
221 // Create a FIFO on the fly to handle any discrepancies in callback rates. | 202 // Create a FIFO on the fly to handle any discrepancies in callback rates. |
222 if (!audio_fifo_) { | 203 if (!audio_fifo_) { |
223 VLOG(1) << "Audio frame size changed from " << number_of_frames_ << " to " | 204 VLOG(1) << "Audio frame size changed from " << number_of_frames_ << " to " |
224 << number_of_frames << "; adding FIFO to compensate."; | 205 << number_of_frames << "; adding FIFO to compensate."; |
225 audio_fifo_.reset(new AudioPullFifo( | 206 audio_fifo_.reset(new AudioPullFifo( |
226 output_channels_, | 207 output_channels_, |
227 number_of_frames_, | 208 number_of_frames_, |
228 base::Bind(&AUHALStream::ProvideInput, base::Unretained(this)))); | 209 base::Bind(&AUHALStream::ProvideInput, base::Unretained(this)))); |
229 } | 210 } |
230 | |
231 // Synchronous IO is not supported in this state. | |
232 if (input_channels_ > 0) | |
233 input_bus_->Zero(); | |
234 } else { | |
235 if (input_channels_ > 0 && input_buffer_list_) { | |
236 // Get the input data. |input_buffer_list_| is wrapped | |
237 // to point to the data allocated in |input_bus_|. | |
238 OSStatus result = AudioUnitRender(audio_unit_, | |
239 flags, | |
240 output_time_stamp, | |
241 1, | |
242 number_of_frames, | |
243 input_buffer_list_); | |
244 if (result != noErr) | |
245 ZeroBufferList(input_buffer_list_); | |
246 } | |
247 } | 211 } |
248 | 212 |
249 // Make |output_bus_| wrap the output AudioBufferList. | 213 // Make |output_bus_| wrap the output AudioBufferList. |
250 WrapBufferList(io_data, output_bus_.get(), number_of_frames); | 214 WrapBufferList(data, output_bus_.get(), number_of_frames); |
251 | 215 |
252 // Update the playout latency. | 216 // Update the playout latency. |
253 const double playout_latency_frames = GetPlayoutLatency(output_time_stamp); | 217 const double playout_latency_frames = GetPlayoutLatency(output_time_stamp); |
254 current_hardware_pending_bytes_ = static_cast<uint32>( | 218 current_hardware_pending_bytes_ = static_cast<uint32>( |
255 (playout_latency_frames + 0.5) * params_.GetBytesPerFrame()); | 219 (playout_latency_frames + 0.5) * params_.GetBytesPerFrame()); |
256 | 220 |
257 if (audio_fifo_) | 221 if (audio_fifo_) |
258 audio_fifo_->Consume(output_bus_.get(), output_bus_->frames()); | 222 audio_fifo_->Consume(output_bus_.get(), output_bus_->frames()); |
259 else | 223 else |
260 ProvideInput(0, output_bus_.get()); | 224 ProvideInput(0, output_bus_.get()); |
261 | 225 |
262 return noErr; | 226 return noErr; |
263 } | 227 } |
264 | 228 |
265 void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) { | 229 void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) { |
266 base::AutoLock auto_lock(source_lock_); | 230 base::AutoLock auto_lock(source_lock_); |
267 if (!source_) { | 231 if (!source_) { |
268 dest->Zero(); | 232 dest->Zero(); |
269 return; | 233 return; |
270 } | 234 } |
271 | 235 |
272 // Supply the input data and render the output data. | 236 // Supply the input data and render the output data. |
273 source_->OnMoreIOData( | 237 source_->OnMoreData( |
274 input_bus_.get(), | |
275 dest, | 238 dest, |
276 AudioBuffersState(0, | 239 AudioBuffersState(0, |
277 current_hardware_pending_bytes_ + | 240 current_hardware_pending_bytes_ + |
278 frame_delay * params_.GetBytesPerFrame())); | 241 frame_delay * params_.GetBytesPerFrame())); |
279 dest->Scale(volume_); | 242 dest->Scale(volume_); |
280 } | 243 } |
281 | 244 |
282 // AUHAL callback. | 245 // AUHAL callback. |
283 OSStatus AUHALStream::InputProc( | 246 OSStatus AUHALStream::InputProc( |
284 void* user_data, | 247 void* user_data, |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
363 // the bots, probably less so in the wild. | 326 // the bots, probably less so in the wild. |
364 if (now_ns > output_time_ns) | 327 if (now_ns > output_time_ns) |
365 return 0; | 328 return 0; |
366 | 329 |
367 double delay_frames = static_cast<double> | 330 double delay_frames = static_cast<double> |
368 (1e-9 * (output_time_ns - now_ns) * output_format_.mSampleRate); | 331 (1e-9 * (output_time_ns - now_ns) * output_format_.mSampleRate); |
369 | 332 |
370 return (delay_frames + hardware_latency_frames_); | 333 return (delay_frames + hardware_latency_frames_); |
371 } | 334 } |
372 | 335 |
373 void AUHALStream::CreateIOBusses() { | 336 bool AUHALStream::EnableIO() { |
DaleCurtis
2014/05/15 17:53:22
Just inline this with the method below?
no longer working on chromium
2014/05/16 13:52:37
Done.
| |
374 if (input_channels_ > 0) { | |
375 // Allocate storage for the AudioBufferList used for the | |
376 // input data from the input AudioUnit. | |
377 // We allocate enough space for with one AudioBuffer per channel. | |
378 size_t buffer_list_size = offsetof(AudioBufferList, mBuffers[0]) + | |
379 (sizeof(AudioBuffer) * input_channels_); | |
380 input_buffer_list_storage_.reset(new uint8[buffer_list_size]); | |
381 | |
382 input_buffer_list_ = | |
383 reinterpret_cast<AudioBufferList*>(input_buffer_list_storage_.get()); | |
384 input_buffer_list_->mNumberBuffers = input_channels_; | |
385 | |
386 // |input_bus_| allocates the storage for the PCM input data. | |
387 input_bus_ = AudioBus::Create(input_channels_, number_of_frames_); | |
388 | |
389 // Make the AudioBufferList point to the memory in |input_bus_|. | |
390 UInt32 buffer_size_bytes = input_bus_->frames() * sizeof(Float32); | |
391 for (size_t i = 0; i < input_buffer_list_->mNumberBuffers; ++i) { | |
392 input_buffer_list_->mBuffers[i].mNumberChannels = 1; | |
393 input_buffer_list_->mBuffers[i].mDataByteSize = buffer_size_bytes; | |
394 input_buffer_list_->mBuffers[i].mData = input_bus_->channel(i); | |
395 } | |
396 } | |
397 | |
398 // The output bus will wrap the AudioBufferList given to us in | |
399 // the Render() callback. | |
400 DCHECK_GT(output_channels_, 0); | |
401 output_bus_ = AudioBus::CreateWrapper(output_channels_); | |
402 } | |
403 | |
404 bool AUHALStream::EnableIO(bool enable, UInt32 scope) { | |
405 // See Apple technote for details about the EnableIO property. | 337 // See Apple technote for details about the EnableIO property. |
406 // Note that we use bus 1 for input and bus 0 for output: | 338 // Note that we use bus 1 for input and bus 0 for output: |
407 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html | 339 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html |
408 UInt32 enable_IO = enable ? 1 : 0; | 340 UInt32 enable_IO = 1; |
341 UInt32 scope = kAudioUnitScope_Output; | |
409 OSStatus result = AudioUnitSetProperty( | 342 OSStatus result = AudioUnitSetProperty( |
410 audio_unit_, | 343 audio_unit_, |
411 kAudioOutputUnitProperty_EnableIO, | 344 kAudioOutputUnitProperty_EnableIO, |
412 scope, | 345 scope, |
413 (scope == kAudioUnitScope_Input) ? 1 : 0, | 346 0, |
414 &enable_IO, | 347 &enable_IO, |
415 sizeof(enable_IO)); | 348 sizeof(enable_IO)); |
416 return (result == noErr); | 349 return (result == noErr); |
417 } | 350 } |
418 | 351 |
419 bool AUHALStream::SetStreamFormat( | 352 bool AUHALStream::SetStreamFormat( |
420 AudioStreamBasicDescription* desc, | 353 AudioStreamBasicDescription* desc, |
421 int channels, | 354 int channels, |
422 UInt32 scope, | 355 UInt32 scope, |
423 UInt32 element) { | 356 UInt32 element) { |
(...skipping 15 matching lines...) Expand all Loading... | |
439 audio_unit_, | 372 audio_unit_, |
440 kAudioUnitProperty_StreamFormat, | 373 kAudioUnitProperty_StreamFormat, |
441 scope, | 374 scope, |
442 element, | 375 element, |
443 &format, | 376 &format, |
444 sizeof(format)); | 377 sizeof(format)); |
445 return (result == noErr); | 378 return (result == noErr); |
446 } | 379 } |
447 | 380 |
448 bool AUHALStream::ConfigureAUHAL() { | 381 bool AUHALStream::ConfigureAUHAL() { |
449 if (device_ == kAudioObjectUnknown || | 382 if (device_ == kAudioObjectUnknown || output_channels_ == 0) |
450 (input_channels_ == 0 && output_channels_ == 0)) | |
451 return false; | 383 return false; |
452 | 384 |
453 AudioComponentDescription desc = { | 385 AudioComponentDescription desc = { |
454 kAudioUnitType_Output, | 386 kAudioUnitType_Output, |
455 kAudioUnitSubType_HALOutput, | 387 kAudioUnitSubType_HALOutput, |
456 kAudioUnitManufacturer_Apple, | 388 kAudioUnitManufacturer_Apple, |
457 0, | 389 0, |
458 0 | 390 0 |
459 }; | 391 }; |
460 AudioComponent comp = AudioComponentFindNext(0, &desc); | 392 AudioComponent comp = AudioComponentFindNext(0, &desc); |
461 if (!comp) | 393 if (!comp) |
462 return false; | 394 return false; |
463 | 395 |
464 OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_); | 396 OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_); |
465 if (result != noErr) { | 397 if (result != noErr) { |
466 OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed."; | 398 OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed."; |
467 return false; | 399 return false; |
468 } | 400 } |
469 | 401 |
470 // Enable input and output as appropriate. | 402 // Enable output as appropriate. |
471 if (!EnableIO(input_channels_ > 0, kAudioUnitScope_Input)) | 403 if (!EnableIO()) |
472 return false; | |
473 if (!EnableIO(output_channels_ > 0, kAudioUnitScope_Output)) | |
474 return false; | 404 return false; |
475 | 405 |
476 // Set the device to be used with the AUHAL AudioUnit. | 406 // Set the device to be used with the AUHAL AudioUnit. |
477 result = AudioUnitSetProperty( | 407 result = AudioUnitSetProperty( |
478 audio_unit_, | 408 audio_unit_, |
479 kAudioOutputUnitProperty_CurrentDevice, | 409 kAudioOutputUnitProperty_CurrentDevice, |
480 kAudioUnitScope_Global, | 410 kAudioUnitScope_Global, |
481 0, | 411 0, |
482 &device_, | 412 &device_, |
483 sizeof(AudioDeviceID)); | 413 sizeof(AudioDeviceID)); |
484 if (result != noErr) | 414 if (result != noErr) |
485 return false; | 415 return false; |
486 | 416 |
487 // Set stream formats. | 417 // Set stream formats. |
488 // See Apple's tech note for details on the peculiar way that | 418 // See Apple's tech note for details on the peculiar way that |
489 // inputs and outputs are handled in the AUHAL concerning scope and bus | 419 // inputs and outputs are handled in the AUHAL concerning scope and bus |
490 // (element) numbers: | 420 // (element) numbers: |
491 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html | 421 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html |
492 | 422 |
493 if (input_channels_ > 0) { | 423 if (!SetStreamFormat(&output_format_, |
494 if (!SetStreamFormat(&input_format_, | 424 output_channels_, |
495 input_channels_, | 425 kAudioUnitScope_Input, |
496 kAudioUnitScope_Output, | 426 0)) { |
497 1)) | 427 return false; |
498 return false; | |
499 } | |
500 | |
501 if (output_channels_ > 0) { | |
502 if (!SetStreamFormat(&output_format_, | |
503 output_channels_, | |
504 kAudioUnitScope_Input, | |
505 0)) | |
506 return false; | |
507 } | 428 } |
508 | 429 |
509 // Set the buffer frame size. | 430 // Set the buffer frame size. |
510 // WARNING: Setting this value changes the frame size for all output audio | 431 // WARNING: Setting this value changes the frame size for all output audio |
511 // units in the current process. As a result, the AURenderCallback must be | 432 // units in the current process. As a result, the AURenderCallback must be |
512 // able to handle arbitrary buffer sizes and FIFO appropriately. | 433 // able to handle arbitrary buffer sizes and FIFO appropriately. |
513 UInt32 buffer_size = 0; | 434 UInt32 buffer_size = 0; |
514 UInt32 property_size = sizeof(buffer_size); | 435 UInt32 property_size = sizeof(buffer_size); |
515 result = AudioUnitGetProperty(audio_unit_, | 436 result = AudioUnitGetProperty(audio_unit_, |
516 kAudioDevicePropertyBufferFrameSize, | 437 kAudioDevicePropertyBufferFrameSize, |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
559 result = AudioUnitInitialize(audio_unit_); | 480 result = AudioUnitInitialize(audio_unit_); |
560 if (result != noErr) { | 481 if (result != noErr) { |
561 OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed."; | 482 OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed."; |
562 return false; | 483 return false; |
563 } | 484 } |
564 | 485 |
565 return true; | 486 return true; |
566 } | 487 } |
567 | 488 |
568 } // namespace media | 489 } // namespace media |
OLD | NEW |