| Index: media/audio/mac/audio_low_latency_output_mac.cc
|
| ===================================================================
|
| --- media/audio/mac/audio_low_latency_output_mac.cc (revision 104447)
|
| +++ media/audio/mac/audio_low_latency_output_mac.cc (working copy)
|
| @@ -47,7 +47,10 @@
|
| : manager_(manager),
|
| source_(NULL),
|
| output_unit_(0),
|
| - volume_(1) {
|
| + output_device_id_(kAudioDeviceUnknown),
|
| + volume_(1),
|
| + hardware_latency_ms_(0),
|
| + playout_latency_ms_(0) {
|
| // We must have a manager.
|
| DCHECK(manager_);
|
| // A frame is one sample across all channels. In interleaved audio the per
|
| @@ -72,6 +75,16 @@
|
| }
|
|
|
| bool AUAudioOutputStream::Open() {
|
| + // Obtain the current input device selected by the user.
|
| + UInt32 size = sizeof(output_device_id_);
|
| + OSStatus result = AudioHardwareGetProperty(
|
| + kAudioHardwarePropertyDefaultOutputDevice,
|
| + &size,
|
| + &output_device_id_);
|
| + DCHECK_EQ(result, 0);
|
| + if (result)
|
| + return false;
|
| +
|
| // Open and initialize the DefaultOutputUnit.
|
| Component comp;
|
| ComponentDescription desc;
|
| @@ -84,7 +97,7 @@
|
| 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 +108,9 @@
|
| if (result)
|
| return false;
|
|
|
| + // Update the playout device hardware latency.
|
| + UpdateHardwareLatency();
|
| +
|
| return Configure();
|
| }
|
|
|
| @@ -185,11 +201,18 @@
|
| // 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.
|
| + UpdatePlayoutLatency(output_time_stamp);
|
| +
|
| AudioBuffer& buffer = io_data->mBuffers[0];
|
| uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
|
| + uint32 hardware_pending_bytes = (1.0e-3 * playout_latency_ms_ *
|
| + format_.mSampleRate * format_.mBytesPerFrame);
|
| uint32 filled = source_->OnMoreData(
|
| - this, audio_data, buffer.mDataByteSize, AudioBuffersState(0, 0));
|
| + this, audio_data, buffer.mDataByteSize,
|
| + AudioBuffersState(buffer.mDataByteSize, hardware_pending_bytes));
|
|
|
| // Handle channel order for 5.1 audio.
|
| if (format_.mChannelsPerFrame == 6) {
|
| @@ -208,7 +231,7 @@
|
| // 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,7 +241,7 @@
|
| 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() {
|
| @@ -261,3 +284,69 @@
|
|
|
| return nominal_sample_rate;
|
| }
|
| +
|
| +void AUAudioOutputStream::UpdateHardwareLatency() {
|
| + // Get audio unit latency.
|
| + Float64 audio_unit_latency_s = 0;
|
| + UInt32 size = sizeof(audio_unit_latency_s);
|
| + OSStatus result = AudioUnitGetProperty(
|
| + output_unit_, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
|
| + 0, &audio_unit_latency_s, &size);
|
| + if (result) {
|
| + DLOG(WARNING) << "UpdateHardwareLatency: Could not get audio unit latency.";
|
| + audio_unit_latency_s = 0;
|
| + }
|
| +
|
| + // Get audio device latency.
|
| + UInt32 device_latency_frames = 0;
|
| + size = sizeof(device_latency_frames);
|
| + result = AudioDeviceGetProperty(output_device_id_, 0, false,
|
| + kAudioDevicePropertyLatency, &size,
|
| + &device_latency_frames);
|
| + if (result) {
|
| + DLOG(WARNING) << "UpdateHardwareLatency: Could not get device latency.";
|
| + device_latency_frames = 0;
|
| + }
|
| +
|
| + // Get the stream latency.
|
| + UInt32 stream_latency_frames = 0;
|
| + result = AudioDeviceGetPropertyInfo(output_device_id_, 0, false,
|
| + kAudioDevicePropertyStreams, &size, NULL);
|
| + if (!result) {
|
| + scoped_ptr_malloc<AudioStreamID>
|
| + streams(reinterpret_cast<AudioStreamID*>(malloc(size)));
|
| + AudioStreamID* stream_ids = streams.get();
|
| + result = AudioDeviceGetProperty(
|
| + output_device_id_, 0, false,
|
| + kAudioDevicePropertyStreams, &size, stream_ids);
|
| + if (result) {
|
| + DLOG(WARNING) << "UpdateHardwareLatency: Could not get stream id.";
|
| + return;
|
| + }
|
| +
|
| + result = AudioStreamGetProperty(
|
| + stream_ids[0], 0, kAudioStreamPropertyLatency,
|
| + &size, &stream_latency_frames);
|
| + if (result) {
|
| + DLOG(WARNING) << "UpdateHardwareLatency: Could not get stream latency.";
|
| + stream_latency_frames = 0;
|
| + }
|
| + } else {
|
| + DLOG(WARNING) << "UpdateHardwareLatency: Could not get stream id.";
|
| + }
|
| +
|
| + hardware_latency_ms_ = static_cast<uint32>(1.0e3 * audio_unit_latency_s +
|
| + (1.0e3 * (device_latency_frames + stream_latency_frames)
|
| + / format_.mSampleRate) + 0.5);
|
| +}
|
| +
|
| +void AUAudioOutputStream::UpdatePlayoutLatency(
|
| + const AudioTimeStamp* output_time_stamp) {
|
| + // Get the delay between now and when the data is going to hit the hardware.
|
| + UInt64 output_time_ns = AudioConvertHostTimeToNanos(
|
| + output_time_stamp->mHostTime);
|
| + UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
|
| + uint32 delay_ms = static_cast<uint32>(1e-6 * (output_time_ns - now_ns) + 0.5);
|
| +
|
| + playout_latency_ms_ = delay_ms + hardware_latency_ms_;
|
| +}
|
|
|