| Index: media/audio/mac/audio_low_latency_output_mac.cc
|
| diff --git a/media/audio/mac/audio_low_latency_output_mac.cc b/media/audio/mac/audio_low_latency_output_mac.cc
|
| index eb8eb1c74d769eb98713fac561029995b2ac777b..259e240c4236c1a80a5462e52dfd80002cb6fc7b 100644
|
| --- a/media/audio/mac/audio_low_latency_output_mac.cc
|
| +++ b/media/audio/mac/audio_low_latency_output_mac.cc
|
| @@ -47,7 +47,9 @@ AUAudioOutputStream::AUAudioOutputStream(
|
| : manager_(manager),
|
| source_(NULL),
|
| output_unit_(0),
|
| - volume_(1) {
|
| + output_device_id_(kAudioObjectUnknown),
|
| + volume_(1),
|
| + hardware_latency_frames_(0) {
|
| // We must have a manager.
|
| DCHECK(manager_);
|
| // A frame is one sample across all channels. In interleaved audio the per
|
| @@ -72,6 +74,23 @@ AUAudioOutputStream::~AUAudioOutputStream() {
|
| }
|
|
|
| bool AUAudioOutputStream::Open() {
|
| + // Obtain the current input device selected by the user.
|
| + UInt32 size = sizeof(output_device_id_);
|
| + AudioObjectPropertyAddress default_output_device_address = {
|
| + kAudioHardwarePropertyDefaultOutputDevice,
|
| + kAudioObjectPropertyScopeGlobal,
|
| + kAudioObjectPropertyElementMaster
|
| + };
|
| + OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
| + &default_output_device_address,
|
| + 0,
|
| + 0,
|
| + &size,
|
| + &output_device_id_);
|
| + DCHECK_EQ(result, 0);
|
| + if (result)
|
| + return false;
|
| +
|
| // Open and initialize the DefaultOutputUnit.
|
| Component comp;
|
| ComponentDescription desc;
|
| @@ -84,7 +103,7 @@ bool AUAudioOutputStream::Open() {
|
| comp = FindNextComponent(0, &desc);
|
| DCHECK(comp);
|
|
|
| - OSStatus result = OpenAComponent(comp, &output_unit_);
|
| + result = OpenAComponent(comp, &output_unit_);
|
| DCHECK_EQ(result, 0);
|
| if (result)
|
| return false;
|
| @@ -95,6 +114,8 @@ bool AUAudioOutputStream::Open() {
|
| if (result)
|
| return false;
|
|
|
| + hardware_latency_frames_ = GetHardwareLatency();
|
| +
|
| return Configure();
|
| }
|
|
|
| @@ -185,11 +206,18 @@ void AUAudioOutputStream::GetVolume(double* volume) {
|
| // Note to future hackers of this function: Do not add locks here because this
|
| // is running on a real-time thread (for low-latency).
|
| OSStatus AUAudioOutputStream::Render(UInt32 number_of_frames,
|
| - AudioBufferList* io_data) {
|
| + AudioBufferList* io_data,
|
| + const AudioTimeStamp* output_time_stamp) {
|
| + // Update the playout latency.
|
| + double playout_latency_frames = GetPlayoutLatency(output_time_stamp);
|
| +
|
| AudioBuffer& buffer = io_data->mBuffers[0];
|
| uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
|
| + uint32 hardware_pending_bytes = static_cast<uint32>
|
| + ((playout_latency_frames + 0.5) * format_.mBytesPerFrame);
|
| uint32 filled = source_->OnMoreData(
|
| - this, audio_data, buffer.mDataByteSize, AudioBuffersState(0, 0));
|
| + this, audio_data, buffer.mDataByteSize,
|
| + AudioBuffersState(0, hardware_pending_bytes));
|
|
|
| // Handle channel order for 5.1 audio.
|
| if (format_.mChannelsPerFrame == 6) {
|
| @@ -208,7 +236,7 @@ OSStatus AUAudioOutputStream::Render(UInt32 number_of_frames,
|
| // DefaultOutputUnit callback
|
| OSStatus AUAudioOutputStream::InputProc(void* user_data,
|
| AudioUnitRenderActionFlags*,
|
| - const AudioTimeStamp*,
|
| + const AudioTimeStamp* output_time_stamp,
|
| UInt32,
|
| UInt32 number_of_frames,
|
| AudioBufferList* io_data) {
|
| @@ -218,12 +246,12 @@ OSStatus AUAudioOutputStream::InputProc(void* user_data,
|
| if (!audio_output)
|
| return -1;
|
|
|
| - return audio_output->Render(number_of_frames, io_data);
|
| + return audio_output->Render(number_of_frames, io_data, output_time_stamp);
|
| }
|
|
|
| double AUAudioOutputStream::HardwareSampleRate() {
|
| // Determine the default output device's sample-rate.
|
| - AudioDeviceID device_id = kAudioDeviceUnknown;
|
| + AudioDeviceID device_id = kAudioObjectUnknown;
|
| UInt32 info_size = sizeof(device_id);
|
|
|
| AudioObjectPropertyAddress default_output_device_address = {
|
| @@ -261,3 +289,83 @@ double AUAudioOutputStream::HardwareSampleRate() {
|
|
|
| return nominal_sample_rate;
|
| }
|
| +
|
| +double AUAudioOutputStream::GetHardwareLatency() {
|
| + if (!output_unit_ || output_device_id_ == kAudioObjectUnknown) {
|
| + DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
|
| + return 0.0;
|
| + }
|
| +
|
| + // Get audio unit latency.
|
| + Float64 audio_unit_latency_sec = 0.0;
|
| + UInt32 size = sizeof(audio_unit_latency_sec);
|
| + OSStatus result = AudioUnitGetProperty(output_unit_,
|
| + kAudioUnitProperty_Latency,
|
| + kAudioUnitScope_Global,
|
| + 0,
|
| + &audio_unit_latency_sec,
|
| + &size);
|
| + DLOG_IF(WARNING, result != noErr) << "Could not get audio unit latency.";
|
| +
|
| + // Get output audio device latency.
|
| + AudioObjectPropertyAddress property_address = {
|
| + kAudioDevicePropertyLatency,
|
| + kAudioDevicePropertyScopeOutput,
|
| + kAudioObjectPropertyElementMaster
|
| + };
|
| + UInt32 device_latency_frames = 0;
|
| + size = sizeof(device_latency_frames);
|
| + result = AudioObjectGetPropertyData(output_device_id_,
|
| + &property_address,
|
| + 0,
|
| + NULL,
|
| + &size,
|
| + &device_latency_frames);
|
| + DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
|
| +
|
| + // Get the stream latency.
|
| + property_address.mSelector = kAudioDevicePropertyStreams;
|
| + UInt32 stream_latency_frames = 0;
|
| + result = AudioObjectGetPropertyDataSize(output_device_id_,
|
| + &property_address,
|
| + 0,
|
| + NULL,
|
| + &size);
|
| + if (!result) {
|
| + scoped_ptr_malloc<AudioStreamID>
|
| + streams(reinterpret_cast<AudioStreamID*>(malloc(size)));
|
| + AudioStreamID* stream_ids = streams.get();
|
| + result = AudioObjectGetPropertyData(output_device_id_,
|
| + &property_address,
|
| + 0,
|
| + NULL,
|
| + &size,
|
| + stream_ids);
|
| + if (!result) {
|
| + property_address.mSelector = kAudioStreamPropertyLatency;
|
| + result = AudioObjectGetPropertyData(stream_ids[0],
|
| + &property_address,
|
| + 0,
|
| + NULL,
|
| + &size,
|
| + &stream_latency_frames);
|
| + }
|
| + }
|
| + DLOG_IF(WARNING, result != noErr) << "Could not get audio stream latency.";
|
| +
|
| + return static_cast<double>((audio_unit_latency_sec *
|
| + format_.mSampleRate) + device_latency_frames + stream_latency_frames);
|
| +}
|
| +
|
| +double AUAudioOutputStream::GetPlayoutLatency(
|
| + const AudioTimeStamp* output_time_stamp) {
|
| + // Get the delay between the moment getting the callback and the scheduled
|
| + // time stamp that tells when the data is going to be played out.
|
| + UInt64 output_time_ns = AudioConvertHostTimeToNanos(
|
| + output_time_stamp->mHostTime);
|
| + UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
|
| + double delay_frames = static_cast<double>
|
| + (1e-9 * (output_time_ns - now_ns) * format_.mSampleRate);
|
| +
|
| + return (delay_frames + hardware_latency_frames_);
|
| +}
|
|
|