/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "A2dpOffloadCodecSbc.h" #include #include "A2dpBits.h" namespace aidl::android::hardware::bluetooth::audio { /** * SBC Local Capabilities */ enum : bool { kEnableSamplingFrequency44100 = true, kEnableSamplingFrequency48000 = true, }; enum : bool { kEnableChannelModeMono = true, kEnableChannelModeDualChannel = true, kEnableChannelModeStereo = true, kEnableChannelModeJointStereo = true, }; enum : bool { kEnableBlockLength4 = true, kEnableBlockLength8 = true, kEnableBlockLength12 = true, kEnableBlockLength16 = true, }; enum : bool { kEnableSubbands4 = true, kEnableSubbands8 = true, }; enum : bool { kEnableAllocationMethodSnr = true, kEnableAllocationMethodLoudness = true, }; enum : uint8_t { kDefaultMinimumBitpool = 2, kDefaultMaximumBitpool = 250, }; enum : int { kBitdepth = 16, }; /** * SBC Signaling format [A2DP - 4.3] */ // clang-format off constexpr A2dpBits::Range kSamplingFrequency ( 0, 3 ); constexpr A2dpBits::Range kChannelMode ( 4, 7 ); constexpr A2dpBits::Range kBlockLength ( 8, 11 ); constexpr A2dpBits::Range kSubbands ( 12, 13 ); constexpr A2dpBits::Range kAllocationMethod ( 14, 15 ); constexpr A2dpBits::Range kMinimumBitpool ( 16, 23 ); constexpr A2dpBits::Range kMaximumBitpool ( 24, 31 ); constexpr size_t kCapabilitiesSize = 32/8; // clang-format on enum { kSamplingFrequency16000 = kSamplingFrequency.first, kSamplingFrequency32000, kSamplingFrequency44100, kSamplingFrequency48000 }; enum { kChannelModeMono = kChannelMode.first, kChannelModeDualChannel, kChannelModeStereo, kChannelModeJointStereo }; enum { kBlockLength4 = kBlockLength.first, kBlockLength8, kBlockLength12, kBlockLength16 }; enum { kSubbands8 = kSubbands.first, kSubbands4 }; enum { kAllocationMethodSnr = kAllocationMethod.first, kAllocationMethodLoudness }; /** * SBC Conversion functions */ static int GetSamplingFrequencyBit(int32_t sampling_frequency) { switch (sampling_frequency) { case 16000: return kSamplingFrequency16000; case 32000: return kSamplingFrequency32000; case 44100: return kSamplingFrequency44100; case 48000: return kSamplingFrequency48000; default: return -1; } } static int32_t GetSamplingFrequencyValue(int sampling_frequency) { switch (sampling_frequency) { case kSamplingFrequency16000: return 16000; case kSamplingFrequency32000: return 32000; case kSamplingFrequency44100: return 44100; case kSamplingFrequency48000: return 48000; default: return 0; } } static int GetChannelModeBit(ChannelMode channel_mode) { switch (channel_mode) { case ChannelMode::STEREO: return kChannelModeJointStereo | kChannelModeStereo; case ChannelMode::DUALMONO: return kChannelModeDualChannel; case ChannelMode::MONO: return kChannelModeMono; default: return -1; } } static ChannelMode GetChannelModeEnum(int channel_mode) { switch (channel_mode) { case kChannelModeMono: return ChannelMode::MONO; case kChannelModeDualChannel: return ChannelMode::DUALMONO; case kChannelModeStereo: case kChannelModeJointStereo: return ChannelMode::STEREO; default: return ChannelMode::UNKNOWN; } } static int32_t GetBlockLengthValue(int block_length) { switch (block_length) { case kBlockLength4: return 4; case kBlockLength8: return 8; case kBlockLength12: return 12; case kBlockLength16: return 16; default: return 0; } } static int32_t GetSubbandsValue(int subbands) { switch (subbands) { case kSubbands4: return 4; case kSubbands8: return 8; default: return 0; } } static SbcParameters::AllocationMethod GetAllocationMethodEnum( int allocation_method) { switch (allocation_method) { case kAllocationMethodSnr: return SbcParameters::AllocationMethod::SNR; case kAllocationMethodLoudness: default: return SbcParameters::AllocationMethod::LOUDNESS; } } static int32_t GetSamplingFrequencyValue(const A2dpBits& configuration) { return GetSamplingFrequencyValue( configuration.find_active_bit(kSamplingFrequency)); } static int32_t GetBlockLengthValue(const A2dpBits& configuration) { return GetBlockLengthValue(configuration.find_active_bit(kBlockLength)); } static int32_t GetSubbandsValue(const A2dpBits& configuration) { return GetSubbandsValue(configuration.find_active_bit(kSubbands)); } static int GetFrameSize(const A2dpBits& configuration, int bitpool) { const int kSbcHeaderSize = 4; int subbands = GetSubbandsValue(configuration); int blocks = GetBlockLengthValue(configuration); unsigned bits = ((4 * subbands) << !configuration.get(kChannelModeMono)) + ((blocks * bitpool) << configuration.get(kChannelModeDualChannel)) + ((configuration.get(kChannelModeJointStereo) ? subbands : 0)); return kSbcHeaderSize + ((bits + 7) >> 3); } static int GetBitrate(const A2dpBits& configuration, int bitpool) { int sampling_frequency = GetSamplingFrequencyValue(configuration); int subbands = GetSubbandsValue(configuration); int blocks = GetBlockLengthValue(configuration); int bits = 8 * GetFrameSize(configuration, bitpool); return (bits * sampling_frequency) / (blocks * subbands); } static uint8_t GetBitpool(const A2dpBits& configuration, int bitrate) { int bitpool = 0; for (int i = 128; i; i >>= 1) if (bitrate > GetBitrate(configuration, bitpool + i)) { bitpool += i; } return std::clamp(bitpool, 2, 250); } /** * SBC Class implementation */ A2dpOffloadCodecSbc::A2dpOffloadCodecSbc() : A2dpOffloadCodec(info_), info_({.id = CodecId(CodecId::A2dp::SBC), .name = "SBC"}) { info_.transport.set(); auto& a2dp_info = info_.transport.get(); /* --- Setup Capabilities --- */ a2dp_info.capabilities.resize(kCapabilitiesSize); std::fill(begin(a2dp_info.capabilities), end(a2dp_info.capabilities), 0); auto capabilities = A2dpBits(a2dp_info.capabilities); capabilities.set(kSamplingFrequency44100, kEnableSamplingFrequency44100); capabilities.set(kSamplingFrequency48000, kEnableSamplingFrequency48000); capabilities.set(kChannelModeMono, kEnableChannelModeMono); capabilities.set(kChannelModeDualChannel, kEnableChannelModeDualChannel); capabilities.set(kChannelModeStereo, kEnableChannelModeStereo); capabilities.set(kChannelModeJointStereo, kEnableChannelModeJointStereo); capabilities.set(kBlockLength4, kEnableBlockLength4); capabilities.set(kBlockLength8, kEnableBlockLength8); capabilities.set(kBlockLength12, kEnableBlockLength12); capabilities.set(kBlockLength16, kEnableBlockLength16); capabilities.set(kSubbands4, kEnableSubbands4); capabilities.set(kSubbands8, kEnableSubbands8); capabilities.set(kSubbands4, kEnableSubbands4); capabilities.set(kSubbands8, kEnableSubbands8); capabilities.set(kAllocationMethodSnr, kEnableAllocationMethodSnr); capabilities.set(kAllocationMethodLoudness, kEnableAllocationMethodLoudness); capabilities.set(kMinimumBitpool, kDefaultMinimumBitpool); capabilities.set(kMaximumBitpool, kDefaultMaximumBitpool); /* --- Setup Sampling Frequencies --- */ auto& sampling_frequency = a2dp_info.samplingFrequencyHz; for (auto v : {16000, 32000, 44100, 48000}) if (capabilities.get(GetSamplingFrequencyBit(int32_t(v)))) sampling_frequency.push_back(v); /* --- Setup Channel Modes --- */ auto& channel_modes = a2dp_info.channelMode; for (auto v : {ChannelMode::MONO, ChannelMode::DUALMONO, ChannelMode::STEREO}) if (capabilities.get(GetChannelModeBit(v))) channel_modes.push_back(v); /* --- Setup Bitdepth --- */ a2dp_info.bitdepth.push_back(kBitdepth); } A2dpStatus A2dpOffloadCodecSbc::ParseConfiguration( const std::vector& configuration, CodecParameters* codec_parameters, SbcParameters* sbc_parameters) const { auto& a2dp_info = info.transport.get(); if (configuration.size() != a2dp_info.capabilities.size()) return A2dpStatus::BAD_LENGTH; auto config = A2dpBits(configuration); auto lcaps = A2dpBits(a2dp_info.capabilities); /* --- Check Sampling Frequency --- */ int sampling_frequency = config.find_active_bit(kSamplingFrequency); if (sampling_frequency < 0) return A2dpStatus::INVALID_SAMPLING_FREQUENCY; if (!lcaps.get(sampling_frequency)) return A2dpStatus::NOT_SUPPORTED_SAMPLING_FREQUENCY; /* --- Check Channel Mode --- */ int channel_mode = config.find_active_bit(kChannelMode); if (channel_mode < 0) return A2dpStatus::INVALID_CHANNEL_MODE; if (!lcaps.get(channel_mode)) return A2dpStatus::NOT_SUPPORTED_CHANNEL_MODE; /* --- Check Block Length --- */ int block_length = config.find_active_bit(kBlockLength); if (block_length < 0) return A2dpStatus::INVALID_BLOCK_LENGTH; /* --- Check Subbands --- */ int subbands = config.find_active_bit(kSubbands); if (subbands < 0) return A2dpStatus::INVALID_SUBBANDS; if (!lcaps.get(subbands)) return A2dpStatus::NOT_SUPPORTED_SUBBANDS; /* --- Check Allocation Method --- */ int allocation_method = config.find_active_bit(kAllocationMethod); if (allocation_method < 0) return A2dpStatus::INVALID_ALLOCATION_METHOD; if (!lcaps.get(allocation_method)) return A2dpStatus::NOT_SUPPORTED_ALLOCATION_METHOD; /* --- Check Bitpool --- */ uint8_t min_bitpool = config.get(kMinimumBitpool); if (min_bitpool < 2 || min_bitpool > 250) return A2dpStatus::INVALID_MINIMUM_BITPOOL_VALUE; if (min_bitpool < lcaps.get(kMinimumBitpool)) return A2dpStatus::NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE; uint8_t max_bitpool = config.get(kMaximumBitpool); if (max_bitpool < 2 || max_bitpool > 250) return A2dpStatus::INVALID_MAXIMUM_BITPOOL_VALUE; if (max_bitpool > lcaps.get(kMaximumBitpool)) return A2dpStatus::NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE; /* --- Return --- */ codec_parameters->channelMode = GetChannelModeEnum(channel_mode); codec_parameters->samplingFrequencyHz = GetSamplingFrequencyValue(sampling_frequency); codec_parameters->bitdepth = kBitdepth; codec_parameters->minBitrate = GetBitrate(config, min_bitpool); codec_parameters->maxBitrate = GetBitrate(config, max_bitpool); if (sbc_parameters) { sbc_parameters->block_length = GetBlockLengthValue(block_length); sbc_parameters->subbands = GetSubbandsValue(subbands); sbc_parameters->allocation_method = GetAllocationMethodEnum(allocation_method); sbc_parameters->min_bitpool = min_bitpool; sbc_parameters->max_bitpool = max_bitpool; } return A2dpStatus::OK; } bool A2dpOffloadCodecSbc::BuildConfiguration( const std::vector& remote_capabilities, const std::optional& hint, std::vector* configuration) const { auto& a2dp_info = info.transport.get(); if (remote_capabilities.size() != a2dp_info.capabilities.size()) return false; auto lcaps = A2dpBits(a2dp_info.capabilities); auto rcaps = A2dpBits(remote_capabilities); configuration->resize(a2dp_info.capabilities.size()); std::fill(begin(*configuration), end(*configuration), 0); auto config = A2dpBits(*configuration); /* --- Select Sampling Frequency --- */ auto sf_hint = hint ? GetSamplingFrequencyBit(hint->samplingFrequencyHz) : -1; if (sf_hint >= 0 && lcaps.get(sf_hint) && rcaps.get(sf_hint)) config.set(sf_hint); else if (lcaps.get(kSamplingFrequency44100) && rcaps.get(kSamplingFrequency44100)) config.set(kSamplingFrequency44100); else if (lcaps.get(kSamplingFrequency48000) && rcaps.get(kSamplingFrequency48000)) config.set(kSamplingFrequency48000); else return false; /* --- Select Channel Mode --- */ auto cm_hint = hint ? GetChannelModeBit(hint->channelMode) : -1; if (cm_hint >= 0 && lcaps.get(cm_hint) && rcaps.get(cm_hint)) config.set(cm_hint); else if (lcaps.get(kChannelModeJointStereo) && rcaps.get(kChannelModeJointStereo)) config.set(kChannelModeJointStereo); else if (lcaps.get(kChannelModeStereo) && rcaps.get(kChannelModeStereo)) config.set(kChannelModeStereo); else if (lcaps.get(kChannelModeDualChannel) && rcaps.get(kChannelModeDualChannel)) config.set(kChannelModeDualChannel); else if (lcaps.get(kChannelModeMono) && rcaps.get(kChannelModeMono)) config.set(kChannelModeMono); else return false; /* --- Select Block Length --- */ if (lcaps.get(kBlockLength16) && rcaps.get(kBlockLength16)) config.set(kBlockLength16); else if (lcaps.get(kBlockLength12) && rcaps.get(kBlockLength12)) config.set(kBlockLength12); else if (lcaps.get(kBlockLength8) && rcaps.get(kBlockLength8)) config.set(kBlockLength8); else if (lcaps.get(kBlockLength4) && rcaps.get(kBlockLength4)) config.set(kBlockLength4); else return false; /* --- Select Subbands --- */ if (lcaps.get(kSubbands8) && rcaps.get(kSubbands8)) config.set(kSubbands8); else if (lcaps.get(kSubbands4) && rcaps.get(kSubbands4)) config.set(kSubbands4); else return false; /* --- Select Allocation method --- */ if (lcaps.get(kAllocationMethodLoudness) && rcaps.get(kAllocationMethodLoudness)) config.set(kAllocationMethodLoudness); else if (lcaps.get(kAllocationMethodSnr) && rcaps.get(kAllocationMethodSnr)) config.set(kAllocationMethodSnr); else return false; /* --- Select Bitpool --- */ uint8_t min_bitpool = rcaps.get(kMinimumBitpool); uint8_t max_bitpool = rcaps.get(kMaximumBitpool); if (min_bitpool < 2 || min_bitpool > 250 || max_bitpool < 2 || max_bitpool > 250 || min_bitpool > max_bitpool) { min_bitpool = 2; max_bitpool = 250; } min_bitpool = std::max(min_bitpool, uint8_t(lcaps.get(kMinimumBitpool))); max_bitpool = std::max(max_bitpool, uint8_t(lcaps.get(kMaximumBitpool))); if (hint) { min_bitpool = std::max(min_bitpool, GetBitpool(*configuration, hint->minBitrate)); if (hint->maxBitrate && hint->maxBitrate >= hint->minBitrate) max_bitpool = std::min(max_bitpool, GetBitpool(*configuration, hint->maxBitrate)); } config.set(kMinimumBitpool, min_bitpool); config.set(kMaximumBitpool, max_bitpool); return true; } } // namespace aidl::android::hardware::bluetooth::audio