diff options
-rw-r--r-- | content/common/audio_messages.h | 4 | ||||
-rw-r--r-- | media/audio/mac/audio_output_mac.cc | 209 | ||||
-rw-r--r-- | media/audio/mac/audio_output_mac.h | 24 | ||||
-rw-r--r-- | media/base/channel_layout.cc | 57 | ||||
-rw-r--r-- | media/base/channel_layout.h | 31 |
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_ |