summaryrefslogtreecommitdiffstats
path: root/media/audio/mac
diff options
context:
space:
mode:
authorannacc@chromium.org <annacc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-26 17:52:17 +0000
committerannacc@chromium.org <annacc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-26 17:52:17 +0000
commitca393c92c74b62915504e717a57e4169d27fdcd5 (patch)
tree72f208a069f5c3054754e7a48800b6851a3ff024 /media/audio/mac
parent6f060c4d9a8049d5665eb7fda1a3c52dd11c6a6d (diff)
downloadchromium_src-ca393c92c74b62915504e717a57e4169d27fdcd5.zip
chromium_src-ca393c92c74b62915504e717a57e4169d27fdcd5.tar.gz
chromium_src-ca393c92c74b62915504e717a57e4169d27fdcd5.tar.bz2
Surround sound swizzling for Mac based on device channel layout preferences.
This patch gets the surround sound channel layout from the user's device using CoreAudio. It then swizzles the channel layout from the source to match the user's prefered channel layout (if needed). BUG=none. TEST=none. Review URL: http://codereview.chromium.org/7047020 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86851 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio/mac')
-rw-r--r--media/audio/mac/audio_output_mac.cc209
-rw-r--r--media/audio/mac/audio_output_mac.h24
2 files changed, 213 insertions, 20 deletions
diff --git a/media/audio/mac/audio_output_mac.cc b/media/audio/mac/audio_output_mac.cc
index c940b5a..b8e2938 100644
--- a/media/audio/mac/audio_output_mac.cc
+++ b/media/audio/mac/audio_output_mac.cc
@@ -6,6 +6,7 @@
#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "media/audio/audio_util.h"
#include "media/audio/mac/audio_manager_mac.h"
@@ -64,6 +65,8 @@ PCMQueueOutAudioOutputStream::PCMQueueOutAudioOutputStream(
format_.mBytesPerFrame = format_.mBytesPerPacket;
packet_size_ = params.GetPacketSize();
+ num_source_channels_ = params.channels;
+ source_layout_ = params.channel_layout;
if (params.bits_per_sample > 8) {
format_.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
@@ -89,9 +92,57 @@ void PCMQueueOutAudioOutputStream::HandleError(OSStatus err) {
}
bool PCMQueueOutAudioOutputStream::Open() {
+ // Get the default device id.
+ unsigned int device_id = 0;
+ AudioObjectPropertyAddress property_address = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ UInt32 device_id_size = sizeof(device_id);
+ OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+ &property_address, 0, NULL,
+ &device_id_size, &device_id);
+ if (err != noErr) {
+ HandleError(err);
+ return false;
+ }
+ // Get the size of the channel layout.
+ UInt32 core_layout_size;
+ err = AudioDeviceGetPropertyInfo(device_id, 0, false,
+ kAudioDevicePropertyPreferredChannelLayout,
+ &core_layout_size, NULL);
+ if (err != noErr) {
+ HandleError(err);
+ return false;
+ }
+ // Get the device's channel layout.
+ scoped_ptr<AudioChannelLayout> core_channel_layout;
+ core_channel_layout.reset(
+ reinterpret_cast<AudioChannelLayout*>(new char[core_layout_size]));
+ err = AudioDeviceGetProperty(device_id, 0, false,
+ kAudioDevicePropertyPreferredChannelLayout,
+ &core_layout_size, core_channel_layout.get());
+ if (err != noErr) {
+ HandleError(err);
+ return false;
+ }
+
+ num_core_channels_ =
+ static_cast<int>(core_channel_layout->mNumberChannelDescriptions);
+ if (num_core_channels_ == 2 &&
+ ChannelLayoutToChannelCount(source_layout_) > 2) {
+ should_down_mix_ = true;
+ format_.mChannelsPerFrame = num_core_channels_;
+ format_.mBytesPerFrame = (format_.mBitsPerChannel >> 3) *
+ format_.mChannelsPerFrame;
+ format_.mBytesPerPacket = format_.mBytesPerFrame * format_.mFramesPerPacket;
+ } else {
+ should_down_mix_ = false;
+ }
// Create the actual queue object and let the OS use its own thread to
// run its CFRunLoop.
- OSStatus err = AudioQueueNewOutput(&format_, RenderCallback, this, NULL,
+ err = AudioQueueNewOutput(&format_, RenderCallback, this, NULL,
kCFRunLoopCommonModes, 0, &audio_queue_);
if (err != noErr) {
HandleError(err);
@@ -113,6 +164,95 @@ bool PCMQueueOutAudioOutputStream::Open() {
HandleError(err);
return false;
}
+
+ // Capture channel layout in a format we can use.
+ for (int i = 0; i < CHANNELS_MAX; ++i)
+ core_channel_orderings_[i] = kEmptyChannel;
+
+ for (int i = 0; i < num_core_channels_; ++i) {
+ switch (core_channel_layout->mChannelDescriptions[i].mChannelLabel) {
+ case kAudioChannelLabel_Left:
+ core_channel_orderings_[LEFT] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][LEFT];
+ break;
+ case kAudioChannelLabel_Right:
+ core_channel_orderings_[RIGHT] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][RIGHT];
+ break;
+ case kAudioChannelLabel_Center:
+ core_channel_orderings_[CENTER] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][CENTER];
+ break;
+ case kAudioChannelLabel_LFEScreen:
+ core_channel_orderings_[LFE] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][LFE];
+ break;
+ case kAudioChannelLabel_LeftSurround:
+ core_channel_orderings_[SIDE_LEFT] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][SIDE_LEFT];
+ break;
+ case kAudioChannelLabel_RightSurround:
+ core_channel_orderings_[SIDE_RIGHT] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][SIDE_RIGHT];
+ break;
+ case kAudioChannelLabel_LeftCenter:
+ core_channel_orderings_[LEFT_OF_CENTER] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][LEFT_OF_CENTER];
+ break;
+ case kAudioChannelLabel_RightCenter:
+ core_channel_orderings_[RIGHT_OF_CENTER] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][RIGHT_OF_CENTER];
+ break;
+ case kAudioChannelLabel_CenterSurround:
+ core_channel_orderings_[BACK_CENTER] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][BACK_CENTER];
+ break;
+ case kAudioChannelLabel_RearSurroundLeft:
+ core_channel_orderings_[BACK_LEFT] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][BACK_LEFT];
+ break;
+ case kAudioChannelLabel_RearSurroundRight:
+ core_channel_orderings_[BACK_RIGHT] = i;
+ channel_remap_[i] = kChannelOrderings[source_layout_][BACK_RIGHT];
+ break;
+ default:
+ DLOG(WARNING) << "Channel label not supported";
+ break;
+ }
+ }
+
+ // Check if we need to adjust the layout.
+ // If the device has a BACK_LEFT and no SIDE_LEFT and the source has
+ // a SIDE_LEFT but no BACK_LEFT, then move (and preserve the channel).
+ // e.g. CHANNEL_LAYOUT_5POINT1 -> CHANNEL_LAYOUT_5POINT1_BACK
+ CheckForAdjustedLayout(SIDE_LEFT, BACK_LEFT);
+ // Same for SIDE_RIGHT -> BACK_RIGHT.
+ CheckForAdjustedLayout(SIDE_RIGHT, BACK_RIGHT);
+ // Move BACK_LEFT to SIDE_LEFT.
+ // e.g. CHANNEL_LAYOUT_5POINT1_BACK -> CHANNEL_LAYOUT_5POINT1
+ CheckForAdjustedLayout(BACK_LEFT, SIDE_LEFT);
+ // Same for BACK_RIGHT -> SIDE_RIGHT.
+ CheckForAdjustedLayout(BACK_RIGHT, SIDE_RIGHT);
+ // Move SIDE_LEFT to LEFT_OF_CENTER.
+ // e.g. CHANNEL_LAYOUT_7POINT1 -> CHANNEL_LAYOUT_7POINT1_WIDE
+ CheckForAdjustedLayout(SIDE_LEFT, LEFT_OF_CENTER);
+ // Same for SIDE_RIGHT -> RIGHT_OF_CENTER.
+ CheckForAdjustedLayout(SIDE_RIGHT, RIGHT_OF_CENTER);
+ // Move LEFT_OF_CENTER to SIDE_LEFT.
+ // e.g. CHANNEL_LAYOUT_7POINT1_WIDE -> CHANNEL_LAYOUT_7POINT1
+ CheckForAdjustedLayout(LEFT_OF_CENTER, SIDE_LEFT);
+ // Same for RIGHT_OF_CENTER -> SIDE_RIGHT.
+ CheckForAdjustedLayout(RIGHT_OF_CENTER, SIDE_RIGHT);
+
+ // Check if we will need to swizzle from source to device layout (maybe not!).
+ should_swizzle_ = false;
+ for (int i = 0; i < num_core_channels_; ++i) {
+ if (kChannelOrderings[source_layout_][i] != core_channel_orderings_[i]) {
+ should_swizzle_ = true;
+ break;
+ }
+ }
+
return true;
}
@@ -175,23 +315,40 @@ void PCMQueueOutAudioOutputStream::GetVolume(double* volume) {
*volume = volume_;
}
-// Reorder PCM from AAC layout to Core Audio layout.
-// TODO(fbarchard): Switch layout when ffmpeg is updated.
template<class Format>
-static void SwizzleLayout(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
+void PCMQueueOutAudioOutputStream::SwizzleLayout(Format* b, uint32 filled) {
+ Format src_format[num_source_channels_];
+ int filled_channels = (num_core_channels_ < num_source_channels_) ?
+ num_core_channels_ : num_source_channels_;
+ for (uint32 i = 0; i < filled; i += sizeof(src_format),
+ b += num_source_channels_) {
+ // TODO(fbarchard): This could be further optimized with pshufb.
+ memcpy(src_format, b, sizeof(src_format));
+ for (int ch = 0; ch < filled_channels; ++ch) {
+ if (channel_remap_[ch] != kEmptyChannel &&
+ channel_remap_[ch] <= CHANNELS_MAX) {
+ b[ch] = src_format[channel_remap_[ch]];
+ } else {
+ b[ch] = 0;
+ }
+ }
}
}
+bool PCMQueueOutAudioOutputStream::CheckForAdjustedLayout(
+ Channels input_channel,
+ Channels output_channel) {
+ if (core_channel_orderings_[output_channel] > kEmptyChannel &&
+ core_channel_orderings_[input_channel] == kEmptyChannel &&
+ kChannelOrderings[source_layout_][input_channel] > kEmptyChannel &&
+ kChannelOrderings[source_layout_][output_channel] == kEmptyChannel) {
+ channel_remap_[core_channel_orderings_[output_channel]] =
+ kChannelOrderings[source_layout_][input_channel];
+ return true;
+ }
+ return false;
+}
+
// Note to future hackers of this function: Do not add locks here because we
// call out to third party source that might do crazy things including adquire
// external locks or somehow re-enter here because its legal for them to call
@@ -240,14 +397,28 @@ void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this,
static_cast<AudioQueueUserData*>(buffer->mUserData)->empty_buffer = false;
}
- // Handle channel order for 5.1 audio.
- if (audio_stream->format_.mChannelsPerFrame == 6) {
+ if (audio_stream->should_down_mix_) {
+ // Downmixes the L, R, C channels to stereo.
+ if (media::FoldChannels(buffer->mAudioData,
+ filled,
+ audio_stream->num_source_channels_,
+ audio_stream->format_.mBitsPerChannel >> 3,
+ audio_stream->volume_)) {
+ filled = filled * 2 / audio_stream->num_source_channels_;
+ } else {
+ LOG(ERROR) << "Folding failed";
+ }
+ } else if (audio_stream->should_swizzle_) {
+ // Handle channel order for surround sound audio.
if (audio_stream->format_.mBitsPerChannel == 8) {
- SwizzleLayout(reinterpret_cast<uint8*>(buffer->mAudioData), filled);
+ audio_stream->SwizzleLayout(reinterpret_cast<uint8*>(buffer->mAudioData),
+ filled);
} else if (audio_stream->format_.mBitsPerChannel == 16) {
- SwizzleLayout(reinterpret_cast<int16*>(buffer->mAudioData), filled);
+ audio_stream->SwizzleLayout(reinterpret_cast<int16*>(buffer->mAudioData),
+ filled);
} else if (audio_stream->format_.mBitsPerChannel == 32) {
- SwizzleLayout(reinterpret_cast<int32*>(buffer->mAudioData), filled);
+ audio_stream->SwizzleLayout(reinterpret_cast<int32*>(buffer->mAudioData),
+ filled);
}
}
diff --git a/media/audio/mac/audio_output_mac.h b/media/audio/mac/audio_output_mac.h
index 31128bc..1129667 100644
--- a/media/audio/mac/audio_output_mac.h
+++ b/media/audio/mac/audio_output_mac.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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.
@@ -7,6 +7,7 @@
#include <AudioToolbox/AudioFormat.h>
#include <AudioToolbox/AudioQueue.h>
+#include <AudioUnit/AudioUnit.h>
#include "media/audio/audio_io.h"
#include "media/audio/audio_parameters.h"
@@ -37,6 +38,13 @@ class PCMQueueOutAudioOutputStream : public AudioOutputStream {
private:
// The audio is double buffered.
static const uint32 kNumBuffers = 2;
+ static const int kEmptyChannel = -1;
+
+ // Reorder PCM from source layout to device layout found in Core Audio.
+ template<class Format>
+ void SwizzleLayout(Format* b, uint32 filled);
+ // Check and move channels if surround sound layout needs adjusted.
+ bool CheckForAdjustedLayout(Channels input_channel, Channels output_channel);
// The OS calls back here when an audio buffer has been processed.
static void RenderCallback(void* p_this, AudioQueueRef queue,
@@ -62,6 +70,20 @@ class PCMQueueOutAudioOutputStream : public AudioOutputStream {
float volume_;
// Number of bytes yet to be played in audio buffer.
uint32 pending_bytes_;
+ // Number of channels in the source audio.
+ int num_source_channels_;
+ // Source's channel layout for surround sound channels.
+ ChannelLayout source_layout_;
+ // Device's channel layout.
+ int core_channel_orderings_[CHANNELS_MAX];
+ // An array for remapping source to device channel layouts during a swizzle.
+ int channel_remap_[CHANNELS_MAX];
+ // Number of channels in device layout.
+ int num_core_channels_;
+ // A flag to determine if swizzle is needed from source to device layouts.
+ bool should_swizzle_;
+ // A flag to determine if downmix is needed from source to device layouts.
+ bool should_down_mix_;
DISALLOW_COPY_AND_ASSIGN(PCMQueueOutAudioOutputStream);
};