| 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
|
|
|