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