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 |