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 |
deleted file mode 100644 |
index afa480aefb956e44b219e40ba83efe3d2db5a1de..0000000000000000000000000000000000000000 |
--- a/media/audio/mac/audio_low_latency_output_mac.cc |
+++ /dev/null |
@@ -1,416 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "media/audio/mac/audio_low_latency_output_mac.h" |
- |
-#include <CoreServices/CoreServices.h> |
- |
-#include "base/basictypes.h" |
-#include "base/command_line.h" |
-#include "base/logging.h" |
-#include "base/mac/mac_logging.h" |
-#include "media/audio/mac/audio_manager_mac.h" |
-#include "media/base/media_switches.h" |
- |
-namespace media { |
- |
-static std::ostream& operator<<(std::ostream& os, |
- const AudioStreamBasicDescription& format) { |
- os << "sample rate : " << format.mSampleRate << std::endl |
- << "format ID : " << format.mFormatID << std::endl |
- << "format flags : " << format.mFormatFlags << std::endl |
- << "bytes per packet : " << format.mBytesPerPacket << std::endl |
- << "frames per packet : " << format.mFramesPerPacket << std::endl |
- << "bytes per frame : " << format.mBytesPerFrame << std::endl |
- << "channels per frame: " << format.mChannelsPerFrame << std::endl |
- << "bits per channel : " << format.mBitsPerChannel; |
- return os; |
-} |
- |
-static AudioObjectPropertyAddress kDefaultOutputDeviceAddress = { |
- kAudioHardwarePropertyDefaultOutputDevice, |
- kAudioObjectPropertyScopeGlobal, |
- kAudioObjectPropertyElementMaster |
-}; |
- |
-// Overview of operation: |
-// 1) An object of AUAudioOutputStream is created by the AudioManager |
-// factory: audio_man->MakeAudioStream(). |
-// 2) Next some thread will call Open(), at that point the underlying |
-// default output Audio Unit is created and configured. |
-// 3) Then some thread will call Start(source). |
-// Then the Audio Unit is started which creates its own thread which |
-// periodically will call the source for more data as buffers are being |
-// consumed. |
-// 4) At some point some thread will call Stop(), which we handle by directly |
-// stopping the default output Audio Unit. |
-// 6) The same thread that called stop will call Close() where we cleanup |
-// and notify the audio manager, which likely will destroy this object. |
- |
-AUAudioOutputStream::AUAudioOutputStream( |
- AudioManagerMac* manager, const AudioParameters& params) |
- : manager_(manager), |
- source_(NULL), |
- output_unit_(0), |
- output_device_id_(kAudioObjectUnknown), |
- volume_(1), |
- hardware_latency_frames_(0), |
- stopped_(false), |
- audio_bus_(AudioBus::Create(params)) { |
- // We must have a manager. |
- DCHECK(manager_); |
- |
- // A frame is one sample across all channels. In interleaved audio the per |
- // frame fields identify the set of n |channels|. In uncompressed audio, a |
- // packet is always one frame. |
- format_.mSampleRate = params.sample_rate(); |
- format_.mFormatID = kAudioFormatLinearPCM; |
- format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | |
- kLinearPCMFormatFlagIsSignedInteger; |
- format_.mBitsPerChannel = params.bits_per_sample(); |
- format_.mChannelsPerFrame = params.channels(); |
- format_.mFramesPerPacket = 1; |
- format_.mBytesPerPacket = (format_.mBitsPerChannel * params.channels()) / 8; |
- format_.mBytesPerFrame = format_.mBytesPerPacket; |
- format_.mReserved = 0; |
- |
- DVLOG(1) << "Desired ouput format: " << format_; |
- |
- // Calculate the number of sample frames per callback. |
- number_of_frames_ = params.frames_per_buffer(); |
- DVLOG(1) << "Number of frames per callback: " << number_of_frames_; |
-} |
- |
-AUAudioOutputStream::~AUAudioOutputStream() { |
-} |
- |
-bool AUAudioOutputStream::Open() { |
- // Obtain the current input device selected by the user. |
- UInt32 size = sizeof(output_device_id_); |
- OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, |
- &kDefaultOutputDeviceAddress, |
- 0, |
- 0, |
- &size, |
- &output_device_id_); |
- if (result != noErr || output_device_id_ == kAudioObjectUnknown) { |
- OSSTATUS_DLOG(ERROR, result) |
- << "Could not get default audio output device."; |
- return false; |
- } |
- |
- // Open and initialize the DefaultOutputUnit. |
- AudioComponent comp; |
- AudioComponentDescription desc; |
- |
- desc.componentType = kAudioUnitType_Output; |
- desc.componentSubType = kAudioUnitSubType_DefaultOutput; |
- desc.componentManufacturer = kAudioUnitManufacturer_Apple; |
- desc.componentFlags = 0; |
- desc.componentFlagsMask = 0; |
- comp = AudioComponentFindNext(0, &desc); |
- if (!comp) |
- return false; |
- |
- result = AudioComponentInstanceNew(comp, &output_unit_); |
- if (result != noErr) { |
- OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed."; |
- return false; |
- } |
- |
- result = AudioUnitInitialize(output_unit_); |
- if (result != noErr) { |
- OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed."; |
- return false; |
- } |
- |
- hardware_latency_frames_ = GetHardwareLatency(); |
- |
- return Configure(); |
-} |
- |
-bool AUAudioOutputStream::Configure() { |
- // Set the render callback. |
- AURenderCallbackStruct input; |
- input.inputProc = InputProc; |
- input.inputProcRefCon = this; |
- OSStatus result = AudioUnitSetProperty( |
- output_unit_, |
- kAudioUnitProperty_SetRenderCallback, |
- kAudioUnitScope_Global, |
- 0, |
- &input, |
- sizeof(input)); |
- if (result != noErr) { |
- OSSTATUS_DLOG(ERROR, result) |
- << "AudioUnitSetProperty(kAudioUnitProperty_SetRenderCallback) failed."; |
- return false; |
- } |
- |
- // Set the stream format. |
- result = AudioUnitSetProperty( |
- output_unit_, |
- kAudioUnitProperty_StreamFormat, |
- kAudioUnitScope_Input, |
- 0, |
- &format_, |
- sizeof(format_)); |
- if (result != noErr) { |
- OSSTATUS_DLOG(ERROR, result) |
- << "AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed."; |
- return false; |
- } |
- |
- // Set the buffer frame size. |
- // WARNING: Setting this value changes the frame size for all audio units in |
- // the current process. It's imperative that the input and output frame sizes |
- // be the same as the frames_per_buffer() returned by |
- // GetDefaultOutputStreamParameters. |
- // See http://crbug.com/154352 for details. |
- const AudioParameters hw_params = |
- manager_->GetDefaultOutputStreamParameters(); |
- if (number_of_frames_ != static_cast<size_t>(hw_params.frames_per_buffer())) { |
- DLOG(ERROR) << "Audio buffer size does not match hardware buffer size."; |
- return false; |
- } |
- |
- UInt32 buffer_size = number_of_frames_; |
- result = AudioUnitSetProperty( |
- output_unit_, |
- kAudioDevicePropertyBufferFrameSize, |
- kAudioUnitScope_Output, |
- 0, |
- &buffer_size, |
- sizeof(buffer_size)); |
- if (result != noErr) { |
- OSSTATUS_DLOG(ERROR, result) |
- << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed."; |
- return false; |
- } |
- |
- return true; |
-} |
- |
-void AUAudioOutputStream::Close() { |
- if (output_unit_) |
- AudioComponentInstanceDispose(output_unit_); |
- |
- // Inform the audio manager that we have been closed. This can cause our |
- // destruction. |
- manager_->ReleaseOutputStream(this); |
-} |
- |
-void AUAudioOutputStream::Start(AudioSourceCallback* callback) { |
- DCHECK(callback); |
- if (!output_unit_) { |
- DLOG(ERROR) << "Open() has not been called successfully"; |
- return; |
- } |
- |
- stopped_ = false; |
- { |
- base::AutoLock auto_lock(source_lock_); |
- source_ = callback; |
- } |
- |
- AudioOutputUnitStart(output_unit_); |
-} |
- |
-void AUAudioOutputStream::Stop() { |
- if (stopped_) |
- return; |
- |
- AudioOutputUnitStop(output_unit_); |
- |
- base::AutoLock auto_lock(source_lock_); |
- source_ = NULL; |
- stopped_ = true; |
-} |
- |
-void AUAudioOutputStream::SetVolume(double volume) { |
- if (!output_unit_) |
- return; |
- volume_ = static_cast<float>(volume); |
- |
- // TODO(crogers): set volume property |
-} |
- |
-void AUAudioOutputStream::GetVolume(double* volume) { |
- if (!output_unit_) |
- return; |
- *volume = volume_; |
-} |
- |
-// Pulls on our provider to get rendered audio stream. |
-// 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, |
- 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); |
- |
- // Unfortunately AUAudioInputStream and AUAudioOutputStream share the frame |
- // size set by kAudioDevicePropertyBufferFrameSize above on a per process |
- // basis. What this means is that the |number_of_frames| value may be larger |
- // or smaller than the value set during Configure(). In this case either |
- // audio input or audio output will be broken, so just output silence. |
- // TODO(crogers): Figure out what can trigger a change in |number_of_frames|. |
- // See http://crbug.com/154352 for details. |
- if (number_of_frames != static_cast<UInt32>(audio_bus_->frames())) { |
- memset(audio_data, 0, number_of_frames * format_.mBytesPerFrame); |
- return noErr; |
- } |
- |
- int frames_filled = 0; |
- { |
- // Render() shouldn't be called except between AudioOutputUnitStart() and |
- // AudioOutputUnitStop() calls, but crash reports have shown otherwise: |
- // http://crbug.com/178765. We use |source_lock_| to prevent races and |
- // crashes in Render() when |source_| is cleared. |
- base::AutoLock auto_lock(source_lock_); |
- if (!source_) { |
- memset(audio_data, 0, number_of_frames * format_.mBytesPerFrame); |
- return noErr; |
- } |
- |
- frames_filled = source_->OnMoreData( |
- audio_bus_.get(), AudioBuffersState(0, hardware_pending_bytes)); |
- } |
- |
- // Note: If this ever changes to output raw float the data must be clipped and |
- // sanitized since it may come from an untrusted source such as NaCl. |
- audio_bus_->Scale(volume_); |
- audio_bus_->ToInterleaved( |
- frames_filled, format_.mBitsPerChannel / 8, audio_data); |
- |
- return noErr; |
-} |
- |
-// DefaultOutputUnit callback |
-OSStatus AUAudioOutputStream::InputProc(void* user_data, |
- AudioUnitRenderActionFlags*, |
- const AudioTimeStamp* output_time_stamp, |
- UInt32, |
- UInt32 number_of_frames, |
- AudioBufferList* io_data) { |
- AUAudioOutputStream* audio_output = |
- static_cast<AUAudioOutputStream*>(user_data); |
- if (!audio_output) |
- return -1; |
- |
- return audio_output->Render(number_of_frames, io_data, output_time_stamp); |
-} |
- |
-int AUAudioOutputStream::HardwareSampleRate() { |
- // Determine the default output device's sample-rate. |
- AudioDeviceID device_id = kAudioObjectUnknown; |
- UInt32 info_size = sizeof(device_id); |
- OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, |
- &kDefaultOutputDeviceAddress, |
- 0, |
- 0, |
- &info_size, |
- &device_id); |
- if (result != noErr || device_id == kAudioObjectUnknown) { |
- OSSTATUS_DLOG(WARNING, result) |
- << "Could not get default audio output device."; |
- return 0; |
- } |
- |
- Float64 nominal_sample_rate; |
- info_size = sizeof(nominal_sample_rate); |
- |
- AudioObjectPropertyAddress nominal_sample_rate_address = { |
- kAudioDevicePropertyNominalSampleRate, |
- kAudioObjectPropertyScopeGlobal, |
- kAudioObjectPropertyElementMaster |
- }; |
- result = AudioObjectGetPropertyData(device_id, |
- &nominal_sample_rate_address, |
- 0, |
- 0, |
- &info_size, |
- &nominal_sample_rate); |
- if (result != noErr) { |
- OSSTATUS_DLOG(WARNING, result) |
- << "Could not get default sample rate for device: " << device_id; |
- return 0; |
- } |
- |
- return static_cast<int>(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); |
- if (result != noErr) { |
- OSSTATUS_DLOG(WARNING, result) << "Could not get audio unit latency"; |
- return 0.0; |
- } |
- |
- // 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); |
- if (result != noErr) { |
- OSSTATUS_DLOG(WARNING, result) << "Could not get audio unit latency"; |
- return 0.0; |
- } |
- |
- return static_cast<double>((audio_unit_latency_sec * |
- format_.mSampleRate) + device_latency_frames); |
-} |
- |
-double AUAudioOutputStream::GetPlayoutLatency( |
- const AudioTimeStamp* output_time_stamp) { |
- // Ensure mHostTime is valid. |
- if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0) |
- return 0; |
- |
- // 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()); |
- |
- // Prevent overflow leading to huge delay information; occurs regularly on |
- // the bots, probably less so in the wild. |
- if (now_ns > output_time_ns) |
- return 0; |
- |
- double delay_frames = static_cast<double> |
- (1e-9 * (output_time_ns - now_ns) * format_.mSampleRate); |
- |
- return (delay_frames + hardware_latency_frames_); |
-} |
- |
-} // namespace media |