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); | |
chcunningham
2016/09/23 20:53:30
integer division
jameswest
2016/09/29 00:52:24
Removed.
| |
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 |