summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/common/audio_messages.h4
-rw-r--r--media/audio/mac/audio_output_mac.cc209
-rw-r--r--media/audio/mac/audio_output_mac.h24
-rw-r--r--media/base/channel_layout.cc57
-rw-r--r--media/base/channel_layout.h31
5 files changed, 303 insertions, 22 deletions
diff --git a/content/common/audio_messages.h b/content/common/audio_messages.h
index 1ff70a5..a1e7e267 100644
--- a/content/common/audio_messages.h
+++ b/content/common/audio_messages.h
@@ -16,6 +16,7 @@
IPC_ENUM_TRAITS(AudioStreamState)
IPC_ENUM_TRAITS(AudioParameters::Format)
+IPC_ENUM_TRAITS(ChannelLayout)
IPC_STRUCT_TRAITS_BEGIN(AudioBuffersState)
IPC_STRUCT_TRAITS_MEMBER(pending_bytes)
@@ -25,10 +26,11 @@ IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(AudioParameters)
IPC_STRUCT_TRAITS_MEMBER(format)
- IPC_STRUCT_TRAITS_MEMBER(channels)
+ IPC_STRUCT_TRAITS_MEMBER(channel_layout)
IPC_STRUCT_TRAITS_MEMBER(sample_rate)
IPC_STRUCT_TRAITS_MEMBER(bits_per_sample)
IPC_STRUCT_TRAITS_MEMBER(samples_per_packet)
+ IPC_STRUCT_TRAITS_MEMBER(channels)
IPC_STRUCT_TRAITS_END()
// Messages sent from the browser to the renderer.
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);
};
diff --git a/media/base/channel_layout.cc b/media/base/channel_layout.cc
index 8e3da4f..81651bc 100644
--- a/media/base/channel_layout.cc
+++ b/media/base/channel_layout.cc
@@ -23,6 +23,63 @@ static const int kLayoutToChannels[] = {
8, // CHANNEL_LAYOUT_7POINT1_WIDE
2}; // CHANNEL_LAYOUT_STEREO_DOWNMIX
+const int kChannelOrderings[CHANNEL_LAYOUT_MAX][CHANNELS_MAX] = {
+ // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR | StL | StR
+
+ // CHANNEL_LAYOUT_NONE
+ { -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_UNSUPPORTED
+ { -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_MONO
+ { -1 , -1 , 0 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_STEREO
+ { 0 , 1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_2_1
+ { 0 , 1 , -1 , -1 , -1 , -1 , -1 , -1 , 2 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_SURROUND
+ { 0 , 1 , 2 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_4POINT0
+ { 0 , 1 , 2 , -1 , -1 , -1 , -1 , -1 , 3 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_2_2
+ { 0 , 1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 2 , 3 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_QUAD
+ { 0 , 1 , -1 , -1 , 2 , 3 , -1 , -1 , -1 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_5POINT0
+ { 0 , 1 , 2 , -1 , -1 , -1 , -1 , -1 , -1 , 3 , 4 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_5POINT1
+ { 0 , 1 , 2 , 3 , -1 , -1 , -1 , -1 , -1 , 4 , 5 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_5POINT0_BACK
+ { 0 , 1 , 2 , -1 , 3 , 4 , -1 , -1 , -1 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_5POINT1_BACK
+ { 0 , 1 , 2 , 3 , 4 , 5 , -1 , -1 , -1 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_7POINT0
+ { 0 , 1 , 2 , -1 , 5 , 6 , -1 , -1 , -1 , 3 , 4 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_7POINT1
+ { 0 , 1 , 2 , 3 , 6 , 7 , -1 , -1 , -1 , 4 , 5 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_7POINT1_WIDE
+ { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , -1 , -1 , -1 , -1 , -1 },
+
+ // CHANNEL_LAYOUT_STEREO_DOWNMIX
+ { -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 0 , 1 },
+
+ // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR | StL | StR
+ };
+
int ChannelLayoutToChannelCount(ChannelLayout layout) {
return kLayoutToChannels[layout];
}
diff --git a/media/base/channel_layout.h b/media/base/channel_layout.h
index b8e1204..3fa84ba 100644
--- a/media/base/channel_layout.h
+++ b/media/base/channel_layout.h
@@ -52,9 +52,38 @@ enum ChannelLayout {
CHANNEL_LAYOUT_7POINT1_WIDE,
// Stereo L, Stereo R
- CHANNEL_LAYOUT_STEREO_DOWNMIX
+ CHANNEL_LAYOUT_STEREO_DOWNMIX,
+
+ // Total number of layouts.
+ CHANNEL_LAYOUT_MAX
+};
+
+enum Channels {
+ LEFT = 0,
+ RIGHT,
+ CENTER,
+ LFE,
+ BACK_LEFT,
+ BACK_RIGHT,
+ LEFT_OF_CENTER,
+ RIGHT_OF_CENTER,
+ BACK_CENTER,
+ SIDE_LEFT,
+ SIDE_RIGHT,
+ STEREO_LEFT,
+ STEREO_RIGHT,
+ CHANNELS_MAX
};
+// The channel orderings for each layout as specified by FFmpeg.
+// Values represent the index of each channel in each layout. For example, the
+// left side surround sound channel in FFmpeg's 5.1 layout is in the 5th
+// position (because the order is L, R, C, LFE, LS, RS), so
+// kChannelOrderings[CHANNEL_LAYOUT_5POINT1][SIDE_LEFT] = 4;
+// Values of -1 mean the channel at that index is not used for that layout.
+extern const int kChannelOrderings[CHANNEL_LAYOUT_MAX][CHANNELS_MAX];
+
+// Returns the number of channels in a given ChannelLayout.
int ChannelLayoutToChannelCount(ChannelLayout layout);
#endif // MEDIA_BASE_CHANNEL_LAYOUT_H_