| 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 <algorithm> |
| 10 #include <string> |
| 11 |
| 9 #include "base/bind.h" | 12 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
| 11 #include "base/logging.h" | 14 #include "base/logging.h" |
| 12 #include "base/mac/mac_logging.h" | 15 #include "base/mac/mac_logging.h" |
| 13 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
| 14 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
| 15 #include "base/time/time.h" | |
| 16 #include "base/trace_event/trace_event.h" | 18 #include "base/trace_event/trace_event.h" |
| 17 #include "media/audio/mac/audio_manager_mac.h" | 19 #include "media/audio/mac/audio_manager_mac.h" |
| 18 #include "media/base/audio_pull_fifo.h" | 20 #include "media/base/audio_pull_fifo.h" |
| 21 #include "media/base/audio_timestamp_helper.h" |
| 19 | 22 |
| 20 namespace media { | 23 namespace media { |
| 21 | 24 |
| 22 static void WrapBufferList(AudioBufferList* buffer_list, | 25 static void WrapBufferList(AudioBufferList* buffer_list, |
| 23 AudioBus* bus, | 26 AudioBus* bus, |
| 24 int frames) { | 27 int frames) { |
| 25 DCHECK(buffer_list); | 28 DCHECK(buffer_list); |
| 26 DCHECK(bus); | 29 DCHECK(bus); |
| 27 const int channels = bus->channels(); | 30 const int channels = bus->channels(); |
| 28 const int buffer_list_channels = buffer_list->mNumberBuffers; | 31 const int buffer_list_channels = buffer_list->mNumberBuffers; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 44 const AudioManager::LogCallback& log_callback) | 47 const AudioManager::LogCallback& log_callback) |
| 45 : manager_(manager), | 48 : manager_(manager), |
| 46 params_(params), | 49 params_(params), |
| 47 output_channels_(params_.channels()), | 50 output_channels_(params_.channels()), |
| 48 number_of_frames_(params_.frames_per_buffer()), | 51 number_of_frames_(params_.frames_per_buffer()), |
| 49 number_of_frames_requested_(0), | 52 number_of_frames_requested_(0), |
| 50 source_(NULL), | 53 source_(NULL), |
| 51 device_(device), | 54 device_(device), |
| 52 audio_unit_(0), | 55 audio_unit_(0), |
| 53 volume_(1), | 56 volume_(1), |
| 54 hardware_latency_frames_(0), | |
| 55 stopped_(true), | 57 stopped_(true), |
| 56 current_hardware_pending_bytes_(0), | |
| 57 current_lost_frames_(0), | 58 current_lost_frames_(0), |
| 58 last_sample_time_(0.0), | 59 last_sample_time_(0.0), |
| 59 last_number_of_frames_(0), | 60 last_number_of_frames_(0), |
| 60 total_lost_frames_(0), | 61 total_lost_frames_(0), |
| 61 largest_glitch_frames_(0), | 62 largest_glitch_frames_(0), |
| 62 glitches_detected_(0), | 63 glitches_detected_(0), |
| 63 log_callback_(log_callback) { | 64 log_callback_(log_callback) { |
| 64 // We must have a manager. | 65 // We must have a manager. |
| 65 DCHECK(manager_); | 66 DCHECK(manager_); |
| 66 CHECK(!log_callback_.Equals(AudioManager::LogCallback())); | 67 CHECK(!log_callback_.Equals(AudioManager::LogCallback())); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 110 return false; | 111 return false; |
| 111 } | 112 } |
| 112 | 113 |
| 113 // The output bus will wrap the AudioBufferList given to us in | 114 // The output bus will wrap the AudioBufferList given to us in |
| 114 // the Render() callback. | 115 // the Render() callback. |
| 115 DCHECK_GT(output_channels_, 0); | 116 DCHECK_GT(output_channels_, 0); |
| 116 output_bus_ = AudioBus::CreateWrapper(output_channels_); | 117 output_bus_ = AudioBus::CreateWrapper(output_channels_); |
| 117 | 118 |
| 118 bool configured = ConfigureAUHAL(); | 119 bool configured = ConfigureAUHAL(); |
| 119 if (configured) | 120 if (configured) |
| 120 hardware_latency_frames_ = GetHardwareLatency(); | 121 hardware_latency_ = GetHardwareLatency(); |
| 121 | 122 |
| 122 return configured; | 123 return configured; |
| 123 } | 124 } |
| 124 | 125 |
| 125 void AUHALStream::Close() { | 126 void AUHALStream::Close() { |
| 126 DCHECK(thread_checker_.CalledOnValidThread()); | 127 DCHECK(thread_checker_.CalledOnValidThread()); |
| 127 DVLOG(1) << "Close"; | 128 DVLOG(1) << "Close"; |
| 128 CloseAudioUnit(); | 129 CloseAudioUnit(); |
| 129 // Inform the audio manager that we have been closed. This will cause our | 130 // Inform the audio manager that we have been closed. This will cause our |
| 130 // destruction. Also include the device ID as a signal to the audio manager | 131 // destruction. Also include the device ID as a signal to the audio manager |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 audio_fifo_.reset(new AudioPullFifo( | 231 audio_fifo_.reset(new AudioPullFifo( |
| 231 output_channels_, | 232 output_channels_, |
| 232 number_of_frames_, | 233 number_of_frames_, |
| 233 base::Bind(&AUHALStream::ProvideInput, base::Unretained(this)))); | 234 base::Bind(&AUHALStream::ProvideInput, base::Unretained(this)))); |
| 234 } | 235 } |
| 235 } | 236 } |
| 236 | 237 |
| 237 // Make |output_bus_| wrap the output AudioBufferList. | 238 // Make |output_bus_| wrap the output AudioBufferList. |
| 238 WrapBufferList(data, output_bus_.get(), number_of_frames); | 239 WrapBufferList(data, output_bus_.get(), number_of_frames); |
| 239 | 240 |
| 240 // Update the playout latency. | 241 current_playout_time_ = GetPlayoutTime(output_time_stamp); |
| 241 const double playout_latency_frames = GetPlayoutLatency(output_time_stamp); | |
| 242 current_hardware_pending_bytes_ = static_cast<uint32_t>( | |
| 243 (playout_latency_frames + 0.5) * params_.GetBytesPerFrame()); | |
| 244 | 242 |
| 245 if (audio_fifo_) | 243 if (audio_fifo_) |
| 246 audio_fifo_->Consume(output_bus_.get(), output_bus_->frames()); | 244 audio_fifo_->Consume(output_bus_.get(), output_bus_->frames()); |
| 247 else | 245 else |
| 248 ProvideInput(0, output_bus_.get()); | 246 ProvideInput(0, output_bus_.get()); |
| 249 | 247 |
| 250 last_number_of_frames_ = number_of_frames; | 248 last_number_of_frames_ = number_of_frames; |
| 251 | 249 |
| 252 return noErr; | 250 return noErr; |
| 253 } | 251 } |
| 254 | 252 |
| 255 void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) { | 253 void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) { |
| 256 base::AutoLock auto_lock(source_lock_); | 254 base::AutoLock auto_lock(source_lock_); |
| 257 if (!source_) { | 255 if (!source_) { |
| 258 dest->Zero(); | 256 dest->Zero(); |
| 259 return; | 257 return; |
| 260 } | 258 } |
| 261 | 259 |
| 260 const base::TimeTicks playout_time = |
| 261 current_playout_time_ + |
| 262 AudioTimestampHelper::FramesToTime(frame_delay, params_.sample_rate()); |
| 263 const base::TimeTicks now = base::TimeTicks::Now(); |
| 264 const base::TimeDelta delay = playout_time - now; |
| 265 |
| 262 // Supply the input data and render the output data. | 266 // Supply the input data and render the output data. |
| 263 source_->OnMoreData(dest, current_hardware_pending_bytes_ + | 267 source_->OnMoreData(delay, now, current_lost_frames_, dest); |
| 264 frame_delay * params_.GetBytesPerFrame(), | |
| 265 current_lost_frames_); | |
| 266 dest->Scale(volume_); | 268 dest->Scale(volume_); |
| 267 current_lost_frames_ = 0; | 269 current_lost_frames_ = 0; |
| 268 } | 270 } |
| 269 | 271 |
| 270 // AUHAL callback. | 272 // AUHAL callback. |
| 271 OSStatus AUHALStream::InputProc( | 273 OSStatus AUHALStream::InputProc( |
| 272 void* user_data, | 274 void* user_data, |
| 273 AudioUnitRenderActionFlags* flags, | 275 AudioUnitRenderActionFlags* flags, |
| 274 const AudioTimeStamp* output_time_stamp, | 276 const AudioTimeStamp* output_time_stamp, |
| 275 UInt32 bus_number, | 277 UInt32 bus_number, |
| 276 UInt32 number_of_frames, | 278 UInt32 number_of_frames, |
| 277 AudioBufferList* io_data) { | 279 AudioBufferList* io_data) { |
| 278 // Dispatch to our class method. | 280 // Dispatch to our class method. |
| 279 AUHALStream* audio_output = | 281 AUHALStream* audio_output = |
| 280 static_cast<AUHALStream*>(user_data); | 282 static_cast<AUHALStream*>(user_data); |
| 281 if (!audio_output) | 283 if (!audio_output) |
| 282 return -1; | 284 return -1; |
| 283 | 285 |
| 284 return audio_output->Render( | 286 return audio_output->Render( |
| 285 flags, | 287 flags, |
| 286 output_time_stamp, | 288 output_time_stamp, |
| 287 bus_number, | 289 bus_number, |
| 288 number_of_frames, | 290 number_of_frames, |
| 289 io_data); | 291 io_data); |
| 290 } | 292 } |
| 291 | 293 |
| 292 double AUHALStream::GetHardwareLatency() { | 294 base::TimeDelta AUHALStream::GetHardwareLatency() { |
| 293 if (!audio_unit_ || device_ == kAudioObjectUnknown) { | 295 if (!audio_unit_ || device_ == kAudioObjectUnknown) { |
| 294 DLOG(WARNING) << "AudioUnit is NULL or device ID is unknown"; | 296 DLOG(WARNING) << "AudioUnit is NULL or device ID is unknown"; |
| 295 return 0.0; | 297 return base::TimeDelta(); |
| 296 } | 298 } |
| 297 | 299 |
| 298 // Get audio unit latency. | 300 // Get audio unit latency. |
| 299 Float64 audio_unit_latency_sec = 0.0; | 301 Float64 audio_unit_latency_sec = 0.0; |
| 300 UInt32 size = sizeof(audio_unit_latency_sec); | 302 UInt32 size = sizeof(audio_unit_latency_sec); |
| 301 OSStatus result = AudioUnitGetProperty( | 303 OSStatus result = AudioUnitGetProperty( |
| 302 audio_unit_, | 304 audio_unit_, |
| 303 kAudioUnitProperty_Latency, | 305 kAudioUnitProperty_Latency, |
| 304 kAudioUnitScope_Global, | 306 kAudioUnitScope_Global, |
| 305 0, | 307 0, |
| 306 &audio_unit_latency_sec, | 308 &audio_unit_latency_sec, |
| 307 &size); | 309 &size); |
| 308 if (result != noErr) { | 310 if (result != noErr) { |
| 309 OSSTATUS_DLOG(WARNING, result) << "Could not get AudioUnit latency"; | 311 OSSTATUS_DLOG(WARNING, result) << "Could not get AudioUnit latency"; |
| 310 return 0.0; | 312 return base::TimeDelta(); |
| 311 } | 313 } |
| 312 | 314 |
| 313 // Get output audio device latency. | 315 // Get output audio device latency. |
| 314 static const AudioObjectPropertyAddress property_address = { | 316 static const AudioObjectPropertyAddress property_address = { |
| 315 kAudioDevicePropertyLatency, | 317 kAudioDevicePropertyLatency, |
| 316 kAudioDevicePropertyScopeOutput, | 318 kAudioDevicePropertyScopeOutput, |
| 317 kAudioObjectPropertyElementMaster | 319 kAudioObjectPropertyElementMaster |
| 318 }; | 320 }; |
| 319 | 321 |
| 320 UInt32 device_latency_frames = 0; | 322 UInt32 device_latency_frames = 0; |
| 321 size = sizeof(device_latency_frames); | 323 size = sizeof(device_latency_frames); |
| 322 result = AudioObjectGetPropertyData( | 324 result = AudioObjectGetPropertyData( |
| 323 device_, | 325 device_, |
| 324 &property_address, | 326 &property_address, |
| 325 0, | 327 0, |
| 326 NULL, | 328 NULL, |
| 327 &size, | 329 &size, |
| 328 &device_latency_frames); | 330 &device_latency_frames); |
| 329 if (result != noErr) { | 331 if (result != noErr) { |
| 330 OSSTATUS_DLOG(WARNING, result) << "Could not get audio device latency"; | 332 OSSTATUS_DLOG(WARNING, result) << "Could not get audio device latency"; |
| 331 return 0.0; | 333 return base::TimeDelta(); |
| 332 } | 334 } |
| 333 | 335 |
| 334 return static_cast<double>((audio_unit_latency_sec * | 336 int latency_frames = audio_unit_latency_sec * output_format_.mSampleRate + |
| 335 output_format_.mSampleRate) + device_latency_frames); | 337 device_latency_frames; |
| 338 |
| 339 return AudioTimestampHelper::FramesToTime(latency_frames, |
| 340 params_.sample_rate()); |
| 336 } | 341 } |
| 337 | 342 |
| 338 double AUHALStream::GetPlayoutLatency( | 343 base::TimeTicks AUHALStream::GetPlayoutTime( |
| 339 const AudioTimeStamp* output_time_stamp) { | 344 const AudioTimeStamp* output_time_stamp) { |
| 340 // Ensure mHostTime is valid. | 345 // A platform bug has been observed where the platform sometimes reports that |
| 346 // the next frames will be output at an invalid time or a time in the past. |
| 347 // Because the target playout time cannot be invalid or in the past, return |
| 348 // "now" in these cases. |
| 341 if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0) | 349 if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0) |
| 342 return 0; | 350 return base::TimeTicks::Now(); |
| 343 | 351 |
| 344 // Get the delay between the moment getting the callback and the scheduled | 352 return std::max(base::TimeTicks::FromMachAbsoluteTime( |
| 345 // time stamp that tells when the data is going to be played out. | 353 output_time_stamp->mHostTime), |
| 346 UInt64 output_time_ns = AudioConvertHostTimeToNanos( | 354 base::TimeTicks::Now()) + |
| 347 output_time_stamp->mHostTime); | 355 hardware_latency_; |
| 348 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); | |
| 349 | |
| 350 // Prevent overflow leading to huge delay information; occurs regularly on | |
| 351 // the bots, probably less so in the wild. | |
| 352 if (now_ns > output_time_ns) | |
| 353 return 0; | |
| 354 | |
| 355 double delay_frames = static_cast<double> | |
| 356 (1e-9 * (output_time_ns - now_ns) * output_format_.mSampleRate); | |
| 357 | |
| 358 return (delay_frames + hardware_latency_frames_); | |
| 359 } | 356 } |
| 360 | 357 |
| 361 void AUHALStream::UpdatePlayoutTimestamp(const AudioTimeStamp* timestamp) { | 358 void AUHALStream::UpdatePlayoutTimestamp(const AudioTimeStamp* timestamp) { |
| 362 if ((timestamp->mFlags & kAudioTimeStampSampleTimeValid) == 0) | 359 if ((timestamp->mFlags & kAudioTimeStampSampleTimeValid) == 0) |
| 363 return; | 360 return; |
| 364 | 361 |
| 365 if (last_sample_time_) { | 362 if (last_sample_time_) { |
| 366 DCHECK_NE(0U, last_number_of_frames_); | 363 DCHECK_NE(0U, last_number_of_frames_); |
| 367 UInt32 diff = | 364 UInt32 diff = |
| 368 static_cast<UInt32>(timestamp->mSampleTime - last_sample_time_); | 365 static_cast<UInt32>(timestamp->mSampleTime - last_sample_time_); |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 556 OSStatus result = AudioUnitUninitialize(audio_unit_); | 553 OSStatus result = AudioUnitUninitialize(audio_unit_); |
| 557 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 554 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
| 558 << "AudioUnitUninitialize() failed."; | 555 << "AudioUnitUninitialize() failed."; |
| 559 result = AudioComponentInstanceDispose(audio_unit_); | 556 result = AudioComponentInstanceDispose(audio_unit_); |
| 560 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 557 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
| 561 << "AudioComponentInstanceDispose() failed."; | 558 << "AudioComponentInstanceDispose() failed."; |
| 562 audio_unit_ = 0; | 559 audio_unit_ = 0; |
| 563 } | 560 } |
| 564 | 561 |
| 565 } // namespace media | 562 } // namespace media |
| OLD | NEW |