summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorcrogers@google.com <crogers@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-29 01:33:48 +0000
committercrogers@google.com <crogers@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-29 01:33:48 +0000
commit7aee6c21c1cba5ed857c76f405e1b6b1e4509151 (patch)
tree203282c9791739ac9a4b58741400c49ccdef1735 /media
parentdf6cf1addfa73b6955ef83a641d929f0be9387ab (diff)
downloadchromium_src-7aee6c21c1cba5ed857c76f405e1b6b1e4509151.zip
chromium_src-7aee6c21c1cba5ed857c76f405e1b6b1e4509151.tar.gz
chromium_src-7aee6c21c1cba5ed857c76f405e1b6b1e4509151.tar.bz2
Add a low-latency AudioOutputStream implementation for Mac OS X.
BUG=none TEST=none (tested locally to verify this code plays audio with much lower latency than the current PCMQueueOutAudioOutputStream implementation) Review URL: http://codereview.chromium.org/6350019 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@73058 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/audio/audio_io.h5
-rw-r--r--media/audio/audio_util.h17
-rw-r--r--media/audio/mac/audio_low_latency_output_mac.cc245
-rw-r--r--media/audio/mac/audio_low_latency_output_mac.h72
-rw-r--r--media/audio/mac/audio_manager_mac.cc21
-rw-r--r--media/audio/mac/audio_manager_mac.h2
-rw-r--r--media/media.gyp3
7 files changed, 353 insertions, 12 deletions
diff --git a/media/audio/audio_io.h b/media/audio/audio_io.h
index cadc5d3..787020c 100644
--- a/media/audio/audio_io.h
+++ b/media/audio/audio_io.h
@@ -74,6 +74,8 @@ class AudioOutputStream {
virtual void OnError(AudioOutputStream* stream, int code) = 0;
};
+ virtual ~AudioOutputStream() {}
+
// Open the stream. false is returned if the stream cannot be opened.
virtual bool Open() = 0;
@@ -97,9 +99,6 @@ class AudioOutputStream {
// Close the stream. This also generates AudioSourceCallback::OnClose().
// After calling this method, the object should not be used anymore.
virtual void Close() = 0;
-
- protected:
- virtual ~AudioOutputStream() {}
};
// Models an audio sink receiving recorded audio from the audio driver.
diff --git a/media/audio/audio_util.h b/media/audio/audio_util.h
index a6ba75f..274c82c 100644
--- a/media/audio/audio_util.h
+++ b/media/audio/audio_util.h
@@ -74,6 +74,23 @@ void InterleaveFloatToInt16(const std::vector<float*>& source,
int16* destination,
size_t number_of_frames);
+// Reorder PCM from AAC layout to Core Audio 5.1 layout.
+// TODO(fbarchard): Switch layout when ffmpeg is updated.
+template<class Format>
+void SwizzleCoreAudioLayout5_1(Format* b, uint32 filled) {
+ static const int kNumSurroundChannels = 6;
+ Format aac[kNumSurroundChannels];
+ for (uint32 i = 0; i < filled; i += sizeof(aac), b += kNumSurroundChannels) {
+ memcpy(aac, b, sizeof(aac));
+ b[0] = aac[1]; // L
+ b[1] = aac[2]; // R
+ b[2] = aac[0]; // C
+ b[3] = aac[5]; // LFE
+ b[4] = aac[3]; // Ls
+ b[5] = aac[4]; // Rs
+ }
+}
+
} // namespace media
#endif // MEDIA_AUDIO_AUDIO_UTIL_H_
diff --git a/media/audio/mac/audio_low_latency_output_mac.cc b/media/audio/mac/audio_low_latency_output_mac.cc
new file mode 100644
index 0000000..2a8e38a
--- /dev/null
+++ b/media/audio/mac/audio_low_latency_output_mac.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 2011 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 "base/basictypes.h"
+#include "base/logging.h"
+#include "media/audio/audio_util.h"
+#include "media/audio/mac/audio_manager_mac.h"
+
+using media::SwizzleCoreAudioLayout5_1;
+
+// 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, AudioParameters params)
+ : manager_(manager),
+ source_(NULL),
+ output_unit_(0),
+ volume_(1) {
+ // 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;
+
+ // Calculate the number of sample frames per callback.
+ number_of_frames_ = params.GetPacketSize() / format_.mBytesPerPacket;
+}
+
+AUAudioOutputStream::~AUAudioOutputStream() {
+}
+
+bool AUAudioOutputStream::Open() {
+ // Open and initialize the DefaultOutputUnit.
+ Component comp;
+ ComponentDescription desc;
+
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+ comp = FindNextComponent(0, &desc);
+ DCHECK(comp);
+
+ OSStatus result = OpenAComponent(comp, &output_unit_);
+ DCHECK_EQ(result, 0);
+ if (result)
+ return false;
+
+ result = AudioUnitInitialize(output_unit_);
+
+ DCHECK_EQ(result, 0);
+ if (result)
+ return false;
+
+ 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));
+
+ DCHECK_EQ(result, 0);
+ if (result)
+ return false;
+
+ // Set the stream format.
+ result = AudioUnitSetProperty(
+ output_unit_,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ 0,
+ &format_,
+ sizeof(format_));
+ DCHECK_EQ(result, 0);
+ if (result)
+ return false;
+
+ // Set the buffer frame size.
+ UInt32 buffer_size = number_of_frames_;
+ result = AudioUnitSetProperty(
+ output_unit_,
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Output,
+ 0,
+ &buffer_size,
+ sizeof(buffer_size));
+ DCHECK_EQ(result, 0);
+ if (result)
+ return false;
+
+ return true;
+}
+
+void AUAudioOutputStream::Close() {
+ if (output_unit_)
+ CloseComponent(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);
+ source_ = callback;
+
+ AudioOutputUnitStart(output_unit_);
+}
+
+void AUAudioOutputStream::Stop() {
+ // We request a synchronous stop, so the next call can take some time. In
+ // the windows implementation we block here as well.
+ source_ = NULL;
+
+ AudioOutputUnitStop(output_unit_);
+}
+
+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) {
+ AudioBuffer& buffer = io_data->mBuffers[0];
+ uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
+ uint32 filled = source_->OnMoreData(
+ this, audio_data, buffer.mDataByteSize, AudioBuffersState(0, 0));
+
+ // Handle channel order for 5.1 audio.
+ if (format_.mChannelsPerFrame == 6) {
+ if (format_.mBitsPerChannel == 8) {
+ SwizzleCoreAudioLayout5_1(reinterpret_cast<uint8*>(audio_data), filled);
+ } else if (format_.mBitsPerChannel == 16) {
+ SwizzleCoreAudioLayout5_1(reinterpret_cast<int16*>(audio_data), filled);
+ } else if (format_.mBitsPerChannel == 32) {
+ SwizzleCoreAudioLayout5_1(reinterpret_cast<int32*>(audio_data), filled);
+ }
+ }
+
+ return noErr;
+}
+
+// DefaultOutputUnit callback
+OSStatus AUAudioOutputStream::InputProc(void* user_data,
+ AudioUnitRenderActionFlags*,
+ const AudioTimeStamp*,
+ UInt32,
+ UInt32 number_of_frames,
+ AudioBufferList* io_data) {
+ AUAudioOutputStream* audio_output =
+ static_cast<AUAudioOutputStream*>(user_data);
+ DCHECK(audio_output);
+ if (!audio_output)
+ return -1;
+
+ return audio_output->Render(number_of_frames, io_data);
+}
+
+double AUAudioOutputStream::HardwareSampleRate() {
+ // Determine the default output device's sample-rate.
+ AudioDeviceID device_id = kAudioDeviceUnknown;
+ UInt32 info_size = sizeof(device_id);
+
+ AudioObjectPropertyAddress default_input_device_address = {
+ kAudioHardwarePropertyDefaultInputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+ &default_input_device_address,
+ 0,
+ 0,
+ &info_size,
+ &device_id);
+ DCHECK_EQ(result, 0);
+ if (result)
+ return 0.0; // error
+
+ 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);
+ DCHECK_EQ(result, 0);
+ if (result)
+ return 0.0; // error
+
+ return nominal_sample_rate;
+}
diff --git a/media/audio/mac/audio_low_latency_output_mac.h b/media/audio/mac/audio_low_latency_output_mac.h
new file mode 100644
index 0000000..446edaa
--- /dev/null
+++ b/media/audio/mac/audio_low_latency_output_mac.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2011 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.
+
+#ifndef MEDIA_AUDIO_MAC_AUDIO_LOW_LATENCY_OUTPUT_MAC_H_
+#define MEDIA_AUDIO_MAC_AUDIO_LOW_LATENCY_OUTPUT_MAC_H_
+
+#include <AudioUnit/AudioUnit.h>
+
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_parameters.h"
+
+class AudioManagerMac;
+
+// Implementation of AudioOuputStream for Mac OS X using the
+// default output Audio Unit present in OS 10.4 and later.
+// The default output Audio Unit is for low-latency audio I/O.
+class AUAudioOutputStream : public AudioOutputStream {
+ public:
+ // The ctor takes all the usual parameters, plus |manager| which is the
+ // the audio manager who is creating this object.
+ AUAudioOutputStream(AudioManagerMac* manager,
+ AudioParameters params);
+ // The dtor is typically called by the AudioManager only and it is usually
+ // triggered by calling AudioOutputStream::Close().
+ virtual ~AUAudioOutputStream();
+
+ // Implementation of AudioOutputStream.
+ virtual bool Open();
+ virtual void Close();
+ virtual void Start(AudioSourceCallback* callback);
+ virtual void Stop();
+ virtual void SetVolume(double volume);
+ virtual void GetVolume(double* volume);
+
+ static double HardwareSampleRate();
+
+ private:
+ // DefaultOutputUnit callback.
+ static OSStatus InputProc(void* user_data,
+ AudioUnitRenderActionFlags* flags,
+ const AudioTimeStamp* time_stamp,
+ UInt32 bus_number,
+ UInt32 number_of_frames,
+ AudioBufferList* io_data);
+
+ OSStatus Render(UInt32 number_of_frames, AudioBufferList* io_data);
+
+ // Sets up the stream format for the default output Audio Unit.
+ bool Configure();
+
+ // Our creator, the audio manager needs to be notified when we close.
+ AudioManagerMac* manager_;
+
+ size_t number_of_frames_;
+
+ // Pointer to the object that will provide the audio samples.
+ AudioSourceCallback* source_;
+
+ // Structure that holds the stream format details such as bitrate.
+ AudioStreamBasicDescription format_;
+
+ // The default output Audio Unit which talks to the audio hardware.
+ AudioUnit output_unit_;
+
+ // Volume level from 0 to 1.
+ float volume_;
+
+ DISALLOW_COPY_AND_ASSIGN(AUAudioOutputStream);
+};
+
+#endif // MEDIA_AUDIO_MAC_AUDIO_LOW_LATENCY_OUTPUT_MAC_H_
diff --git a/media/audio/mac/audio_manager_mac.cc b/media/audio/mac/audio_manager_mac.cc
index 48df6ab..71ce811 100644
--- a/media/audio/mac/audio_manager_mac.cc
+++ b/media/audio/mac/audio_manager_mac.cc
@@ -8,6 +8,7 @@
#include "media/audio/fake_audio_input_stream.h"
#include "media/audio/fake_audio_output_stream.h"
#include "media/audio/mac/audio_input_mac.h"
+#include "media/audio/mac/audio_low_latency_output_mac.h"
#include "media/audio/mac/audio_manager_mac.h"
#include "media/audio/mac/audio_output_mac.h"
#include "media/base/limits.h"
@@ -85,11 +86,8 @@ bool AudioManagerMac::HasAudioInputDevices() {
AudioOutputStream* AudioManagerMac::MakeAudioOutputStream(
AudioParameters params) {
- if (params.format == AudioParameters::AUDIO_MOCK) {
- return FakeAudioOutputStream::MakeFakeStream(params);
- } else if (params.format != AudioParameters::AUDIO_PCM_LINEAR) {
+ if (!params.IsValid())
return NULL;
- }
// Limit the number of audio streams opened. This is to prevent using
// excessive resources for a large number of audio streams. More
@@ -99,8 +97,16 @@ AudioOutputStream* AudioManagerMac::MakeAudioOutputStream(
return NULL;
}
- num_output_streams_++;
- return new PCMQueueOutAudioOutputStream(this, params);
+ if (params.format == AudioParameters::AUDIO_MOCK) {
+ return FakeAudioOutputStream::MakeFakeStream(params);
+ } else if (params.format == AudioParameters::AUDIO_PCM_LINEAR) {
+ num_output_streams_++;
+ return new PCMQueueOutAudioOutputStream(this, params);
+ } else if (params.format == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
+ num_output_streams_++;
+ return new AUAudioOutputStream(this, params);
+ }
+ return NULL;
}
AudioInputStream* AudioManagerMac::MakeAudioInputStream(
@@ -125,8 +131,7 @@ void AudioManagerMac::UnMuteAll() {
}
// Called by the stream when it has been released by calling Close().
-void AudioManagerMac::ReleaseOutputStream(
- PCMQueueOutAudioOutputStream* stream) {
+void AudioManagerMac::ReleaseOutputStream(AudioOutputStream* stream) {
DCHECK(stream);
num_output_streams_--;
delete stream;
diff --git a/media/audio/mac/audio_manager_mac.h b/media/audio/mac/audio_manager_mac.h
index d2fc3f3..1977bac 100644
--- a/media/audio/mac/audio_manager_mac.h
+++ b/media/audio/mac/audio_manager_mac.h
@@ -29,7 +29,7 @@ class AudioManagerMac : public AudioManagerBase {
// Mac-only method to free the streams created by above facoty methods.
// They are called internally by the respective audio stream when it has
// been closed.
- void ReleaseOutputStream(PCMQueueOutAudioOutputStream* stream);
+ void ReleaseOutputStream(AudioOutputStream* stream);
void ReleaseInputStream(PCMQueueInAudioInputStream* stream);
private:
diff --git a/media/media.gyp b/media/media.gyp
index 660898b..e2cdafd 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -57,6 +57,8 @@
'audio/openbsd/audio_manager_openbsd.h',
'audio/mac/audio_input_mac.cc',
'audio/mac/audio_input_mac.h',
+ 'audio/mac/audio_low_latency_output_mac.cc',
+ 'audio/mac/audio_low_latency_output_mac.h',
'audio/mac/audio_manager_mac.cc',
'audio/mac/audio_manager_mac.h',
'audio/mac/audio_output_mac.cc',
@@ -205,6 +207,7 @@
['OS=="mac"', {
'link_settings': {
'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework',
'$(SDKROOT)/System/Library/Frameworks/AudioToolbox.framework',
'$(SDKROOT)/System/Library/Frameworks/CoreAudio.framework',
],