/* * Copyright 2022 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. */ #define LOG_TAG "BTAudioCodecStatusAIDL" #include "codec_status_aidl.h" #include #include #include #include "a2dp_aac_constants.h" #include "a2dp_sbc_constants.h" #include "a2dp_vendor_aptx_constants.h" #include "a2dp_vendor_aptx_hd_constants.h" #include "a2dp_vendor_ldac_constants.h" #include "bta/av/bta_av_int.h" #include "client_interface_aidl.h" namespace bluetooth { namespace audio { namespace aidl { namespace codec { using ::aidl::android::hardware::bluetooth::audio::AacCapabilities; using ::aidl::android::hardware::bluetooth::audio::AacConfiguration; using ::aidl::android::hardware::bluetooth::audio::AacObjectType; using ::aidl::android::hardware::bluetooth::audio::AptxCapabilities; using ::aidl::android::hardware::bluetooth::audio::AptxConfiguration; using ::aidl::android::hardware::bluetooth::audio::AudioCapabilities; using ::aidl::android::hardware::bluetooth::audio::ChannelMode; using ::aidl::android::hardware::bluetooth::audio::CodecCapabilities; using ::aidl::android::hardware::bluetooth::audio::CodecType; using ::aidl::android::hardware::bluetooth::audio::LdacCapabilities; using ::aidl::android::hardware::bluetooth::audio::LdacChannelMode; using ::aidl::android::hardware::bluetooth::audio::LdacConfiguration; using ::aidl::android::hardware::bluetooth::audio::LdacQualityIndex; using ::aidl::android::hardware::bluetooth::audio::OpusCapabilities; using ::aidl::android::hardware::bluetooth::audio::OpusConfiguration; using ::aidl::android::hardware::bluetooth::audio::SbcAllocMethod; using ::aidl::android::hardware::bluetooth::audio::SbcCapabilities; using ::aidl::android::hardware::bluetooth::audio::SbcChannelMode; using ::aidl::android::hardware::bluetooth::audio::SbcConfiguration; namespace { // capabilities from BluetoothAudioSinkClientInterface::GetAudioCapabilities() std::vector audio_hal_capabilities(0); // capabilities that audio HAL supports and frameworks / Bluetooth SoC / runtime // preference would like to use. std::vector offloading_preference(0); template struct identity { typedef T type; }; template bool ContainedInVector(const std::vector& vector, const typename identity::type& target) { return std::find(vector.begin(), vector.end(), target) != vector.end(); } bool sbc_offloading_capability_match(const SbcCapabilities& sbc_capability, const SbcConfiguration& sbc_config) { if (!ContainedInVector(sbc_capability.channelMode, sbc_config.channelMode) || !ContainedInVector(sbc_capability.allocMethod, sbc_config.allocMethod) || !ContainedInVector(sbc_capability.blockLength, sbc_config.blockLength) || !ContainedInVector(sbc_capability.numSubbands, sbc_config.numSubbands) || !ContainedInVector(sbc_capability.bitsPerSample, sbc_config.bitsPerSample) || !ContainedInVector(sbc_capability.sampleRateHz, sbc_config.sampleRateHz) || (sbc_config.minBitpool < sbc_capability.minBitpool || sbc_config.maxBitpool < sbc_config.minBitpool || sbc_capability.maxBitpool < sbc_config.maxBitpool)) { log::warn("software codec={} capability={}", sbc_config.toString(), sbc_capability.toString()); return false; } log::info("offload codec={} capability={}", sbc_config.toString(), sbc_capability.toString()); return true; } bool aac_offloading_capability_match(const AacCapabilities& aac_capability, const AacConfiguration& aac_config) { if (!ContainedInVector(aac_capability.channelMode, aac_config.channelMode) || !ContainedInVector(aac_capability.objectType, aac_config.objectType) || !ContainedInVector(aac_capability.bitsPerSample, aac_config.bitsPerSample) || !ContainedInVector(aac_capability.sampleRateHz, aac_config.sampleRateHz) || (!aac_capability.variableBitRateSupported && aac_config.variableBitRateEnabled)) { log::warn("software codec={} capability={}", aac_config.toString(), aac_capability.toString()); return false; } log::info("offloading codec={} capability={}", aac_config.toString(), aac_capability.toString()); return true; } bool aptx_offloading_capability_match(const AptxCapabilities& aptx_capability, const AptxConfiguration& aptx_config) { if (!ContainedInVector(aptx_capability.channelMode, aptx_config.channelMode) || !ContainedInVector(aptx_capability.bitsPerSample, aptx_config.bitsPerSample) || !ContainedInVector(aptx_capability.sampleRateHz, aptx_config.sampleRateHz)) { log::warn("software codec={} capability={}", aptx_config.toString(), aptx_capability.toString()); return false; } log::info("offloading codec={} capability={}", aptx_config.toString(), aptx_capability.toString()); return true; } bool ldac_offloading_capability_match(const LdacCapabilities& ldac_capability, const LdacConfiguration& ldac_config) { if (!ContainedInVector(ldac_capability.channelMode, ldac_config.channelMode) || !ContainedInVector(ldac_capability.bitsPerSample, ldac_config.bitsPerSample) || !ContainedInVector(ldac_capability.sampleRateHz, ldac_config.sampleRateHz)) { log::warn("software codec={} capability={}", ldac_config.toString(), ldac_capability.toString()); return false; } log::info("offloading codec={} capability={}", ldac_config.toString(), ldac_capability.toString()); return true; } bool opus_offloading_capability_match( const std::optional& opus_capability, const std::optional& opus_config) { if (!ContainedInVector(opus_capability->channelMode, opus_config->channelMode) || !ContainedInVector(opus_capability->frameDurationUs, opus_config->frameDurationUs) || !ContainedInVector(opus_capability->samplingFrequencyHz, opus_config->samplingFrequencyHz)) { log::warn("software codec={} capability={}", opus_config->toString(), opus_capability->toString()); return false; } log::info("offloading codec={} capability={}", opus_config->toString(), opus_capability->toString()); return true; } } // namespace const CodecConfiguration kInvalidCodecConfiguration = {}; int32_t A2dpCodecToHalSampleRate( const btav_a2dp_codec_config_t& a2dp_codec_config) { switch (a2dp_codec_config.sample_rate) { case BTAV_A2DP_CODEC_SAMPLE_RATE_44100: return 44100; case BTAV_A2DP_CODEC_SAMPLE_RATE_48000: return 48000; case BTAV_A2DP_CODEC_SAMPLE_RATE_88200: return 88200; case BTAV_A2DP_CODEC_SAMPLE_RATE_96000: return 96000; case BTAV_A2DP_CODEC_SAMPLE_RATE_176400: return 176400; case BTAV_A2DP_CODEC_SAMPLE_RATE_192000: return 192000; case BTAV_A2DP_CODEC_SAMPLE_RATE_16000: return 16000; case BTAV_A2DP_CODEC_SAMPLE_RATE_24000: return 24000; default: return 0; } } int8_t A2dpCodecToHalBitsPerSample( const btav_a2dp_codec_config_t& a2dp_codec_config) { switch (a2dp_codec_config.bits_per_sample) { case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16: return 16; case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24: return 24; case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32: return 32; default: return 0; } } ChannelMode A2dpCodecToHalChannelMode( const btav_a2dp_codec_config_t& a2dp_codec_config) { switch (a2dp_codec_config.channel_mode) { case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO: return ChannelMode::MONO; case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO: return ChannelMode::STEREO; default: return ChannelMode::UNKNOWN; } } bool A2dpSbcToHalConfig(CodecConfiguration* codec_config, A2dpCodecConfig* a2dp_config) { btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig(); if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_SBC && current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SINK_SBC) { return false; } tBT_A2DP_OFFLOAD a2dp_offload; a2dp_config->getCodecSpecificConfig(&a2dp_offload); codec_config->codecType = CodecType::SBC; SbcConfiguration sbc_config = {}; sbc_config.sampleRateHz = A2dpCodecToHalSampleRate(current_codec); if (sbc_config.sampleRateHz <= 0) { log::error("Unknown SBC sample_rate={}", current_codec.sample_rate); return false; } uint8_t channel_mode = a2dp_offload.codec_info[3] & A2DP_SBC_IE_CH_MD_MSK; switch (channel_mode) { case A2DP_SBC_IE_CH_MD_JOINT: sbc_config.channelMode = SbcChannelMode::JOINT_STEREO; break; case A2DP_SBC_IE_CH_MD_STEREO: sbc_config.channelMode = SbcChannelMode::STEREO; break; case A2DP_SBC_IE_CH_MD_DUAL: sbc_config.channelMode = SbcChannelMode::DUAL; break; case A2DP_SBC_IE_CH_MD_MONO: sbc_config.channelMode = SbcChannelMode::MONO; break; default: log::error("Unknown SBC channel_mode={}", channel_mode); sbc_config.channelMode = SbcChannelMode::UNKNOWN; return false; } uint8_t block_length = a2dp_offload.codec_info[0] & A2DP_SBC_IE_BLOCKS_MSK; switch (block_length) { case A2DP_SBC_IE_BLOCKS_4: sbc_config.blockLength = 4; break; case A2DP_SBC_IE_BLOCKS_8: sbc_config.blockLength = 8; break; case A2DP_SBC_IE_BLOCKS_12: sbc_config.blockLength = 12; break; case A2DP_SBC_IE_BLOCKS_16: sbc_config.blockLength = 16; break; default: log::error("Unknown SBC block_length={}", block_length); return false; } uint8_t sub_bands = a2dp_offload.codec_info[0] & A2DP_SBC_IE_SUBBAND_MSK; switch (sub_bands) { case A2DP_SBC_IE_SUBBAND_4: sbc_config.numSubbands = 4; break; case A2DP_SBC_IE_SUBBAND_8: sbc_config.numSubbands = 8; break; default: log::error("Unknown SBC Subbands={}", sub_bands); return false; } uint8_t alloc_method = a2dp_offload.codec_info[0] & A2DP_SBC_IE_ALLOC_MD_MSK; switch (alloc_method) { case A2DP_SBC_IE_ALLOC_MD_S: sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_S; break; case A2DP_SBC_IE_ALLOC_MD_L: sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_L; break; default: log::error("Unknown SBC alloc_method={}", alloc_method); return false; } sbc_config.minBitpool = a2dp_offload.codec_info[1]; sbc_config.maxBitpool = a2dp_offload.codec_info[2]; sbc_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec); if (sbc_config.bitsPerSample <= 0) { log::error("Unknown SBC bits_per_sample={}", current_codec.bits_per_sample); return false; } codec_config->config.set( sbc_config); return true; } bool A2dpAacToHalConfig(CodecConfiguration* codec_config, A2dpCodecConfig* a2dp_config) { btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig(); if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_AAC && current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SINK_AAC) { return false; } tBT_A2DP_OFFLOAD a2dp_offload; a2dp_config->getCodecSpecificConfig(&a2dp_offload); codec_config->codecType = CodecType::AAC; AacConfiguration aac_config = {}; uint8_t object_type = a2dp_offload.codec_info[0]; switch (object_type) { case A2DP_AAC_OBJECT_TYPE_MPEG2_LC: aac_config.objectType = AacObjectType::MPEG2_LC; break; case A2DP_AAC_OBJECT_TYPE_MPEG4_LC: aac_config.objectType = AacObjectType::MPEG4_LC; break; case A2DP_AAC_OBJECT_TYPE_MPEG4_LTP: aac_config.objectType = AacObjectType::MPEG4_LTP; break; case A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE: aac_config.objectType = AacObjectType::MPEG4_SCALABLE; break; default: log::error("Unknown AAC object_type={}", object_type); return false; } aac_config.sampleRateHz = A2dpCodecToHalSampleRate(current_codec); if (aac_config.sampleRateHz <= 0) { log::error("Unknown AAC sample_rate={}", current_codec.sample_rate); return false; } aac_config.channelMode = A2dpCodecToHalChannelMode(current_codec); if (aac_config.channelMode == ChannelMode::UNKNOWN) { log::error("Unknown AAC channel_mode={}", current_codec.channel_mode); return false; } uint8_t vbr_enabled = a2dp_offload.codec_info[1] & A2DP_AAC_VARIABLE_BIT_RATE_MASK; switch (vbr_enabled) { case A2DP_AAC_VARIABLE_BIT_RATE_ENABLED: aac_config.variableBitRateEnabled = true; break; case A2DP_AAC_VARIABLE_BIT_RATE_DISABLED: aac_config.variableBitRateEnabled = false; break; default: log::error("Unknown AAC VBR={}", vbr_enabled); return false; } aac_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec); if (aac_config.bitsPerSample <= 0) { log::error("Unknown AAC bits_per_sample={}", current_codec.bits_per_sample); return false; } codec_config->config.set( aac_config); return true; } bool A2dpAptxToHalConfig(CodecConfiguration* codec_config, A2dpCodecConfig* a2dp_config) { btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig(); if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_APTX && current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD) { return false; } tBT_A2DP_OFFLOAD a2dp_offload; a2dp_config->getCodecSpecificConfig(&a2dp_offload); if (current_codec.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX) { codec_config->codecType = CodecType::APTX; } else { codec_config->codecType = CodecType::APTX_HD; } AptxConfiguration aptx_config = {}; aptx_config.sampleRateHz = A2dpCodecToHalSampleRate(current_codec); if (aptx_config.sampleRateHz <= 0) { log::error("Unknown aptX sample_rate={}", current_codec.sample_rate); return false; } aptx_config.channelMode = A2dpCodecToHalChannelMode(current_codec); if (aptx_config.channelMode == ChannelMode::UNKNOWN) { log::error("Unknown aptX channel_mode={}", current_codec.channel_mode); return false; } aptx_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec); if (aptx_config.bitsPerSample <= 0) { log::error("Unknown aptX bits_per_sample={}", current_codec.bits_per_sample); return false; } codec_config->config.set( aptx_config); return true; } bool A2dpLdacToHalConfig(CodecConfiguration* codec_config, A2dpCodecConfig* a2dp_config) { btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig(); if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC) { return false; } tBT_A2DP_OFFLOAD a2dp_offload; a2dp_config->getCodecSpecificConfig(&a2dp_offload); codec_config->codecType = CodecType::LDAC; LdacConfiguration ldac_config = {}; ldac_config.sampleRateHz = A2dpCodecToHalSampleRate(current_codec); if (ldac_config.sampleRateHz <= 0) { log::error("Unknown LDAC sample_rate={}", current_codec.sample_rate); return false; } switch (a2dp_offload.codec_info[7]) { case A2DP_LDAC_CHANNEL_MODE_STEREO: ldac_config.channelMode = LdacChannelMode::STEREO; break; case A2DP_LDAC_CHANNEL_MODE_DUAL: ldac_config.channelMode = LdacChannelMode::DUAL; break; case A2DP_LDAC_CHANNEL_MODE_MONO: ldac_config.channelMode = LdacChannelMode::MONO; break; default: log::error("Unknown LDAC channel_mode={}", a2dp_offload.codec_info[7]); ldac_config.channelMode = LdacChannelMode::UNKNOWN; return false; } switch (a2dp_offload.codec_info[6]) { case A2DP_LDAC_QUALITY_HIGH: ldac_config.qualityIndex = LdacQualityIndex::HIGH; break; case A2DP_LDAC_QUALITY_MID: ldac_config.qualityIndex = LdacQualityIndex::MID; break; case A2DP_LDAC_QUALITY_LOW: ldac_config.qualityIndex = LdacQualityIndex::LOW; break; case A2DP_LDAC_QUALITY_ABR_OFFLOAD: ldac_config.qualityIndex = LdacQualityIndex::ABR; break; default: log::error("Unknown LDAC QualityIndex={}", a2dp_offload.codec_info[6]); return false; } ldac_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec); if (ldac_config.bitsPerSample <= 0) { log::error("Unknown LDAC bits_per_sample={}", current_codec.bits_per_sample); return false; } codec_config->config.set( ldac_config); return true; } bool A2dpOpusToHalConfig(CodecConfiguration* codec_config, A2dpCodecConfig* a2dp_config) { btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig(); if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS) { codec_config = {}; return false; } tBT_A2DP_OFFLOAD a2dp_offload; a2dp_config->getCodecSpecificConfig(&a2dp_offload); codec_config->codecType = CodecType::OPUS; OpusConfiguration opus_config = {}; opus_config.pcmBitDepth = A2dpCodecToHalBitsPerSample(current_codec); if (opus_config.pcmBitDepth <= 0) { log::error("Unknown Opus bits_per_sample={}", current_codec.bits_per_sample); return false; } opus_config.samplingFrequencyHz = A2dpCodecToHalSampleRate(current_codec); if (opus_config.samplingFrequencyHz <= 0) { log::error("Unknown Opus sample_rate={}", current_codec.sample_rate); return false; } opus_config.channelMode = A2dpCodecToHalChannelMode(current_codec); if (opus_config.channelMode == ChannelMode::UNKNOWN) { log::error("Unknown Opus channel_mode={}", current_codec.channel_mode); return false; } opus_config.frameDurationUs = 20000; if (opus_config.channelMode == ChannelMode::STEREO) { opus_config.octetsPerFrame = 640; } else { opus_config.octetsPerFrame = 320; } codec_config->config.set( opus_config); return true; } bool UpdateOffloadingCapabilities( const std::vector& framework_preference) { audio_hal_capabilities = BluetoothAudioSinkClientInterface::GetAudioCapabilities( SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH); std::unordered_set codec_type_set; for (auto preference : framework_preference) { switch (preference.codec_type) { case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC: codec_type_set.insert(CodecType::SBC); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC: codec_type_set.insert(CodecType::AAC); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX: codec_type_set.insert(CodecType::APTX); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: codec_type_set.insert(CodecType::APTX_HD); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: codec_type_set.insert(CodecType::LDAC); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3: log::warn("Ignore source codec_type={}, not implemented", preference.codec_type); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: codec_type_set.insert(CodecType::OPUS); break; case BTAV_A2DP_CODEC_INDEX_SINK_SBC: [[fallthrough]]; case BTAV_A2DP_CODEC_INDEX_SINK_AAC: [[fallthrough]]; case BTAV_A2DP_CODEC_INDEX_SINK_LDAC: [[fallthrough]]; case BTAV_A2DP_CODEC_INDEX_SINK_OPUS: log::warn("Ignore sink codec_type={}", preference.codec_type); break; case BTAV_A2DP_CODEC_INDEX_MAX: [[fallthrough]]; default: log::error("Unknown codec_type={}", preference.codec_type); return false; } } offloading_preference.clear(); for (auto capability : audio_hal_capabilities) { auto codec_type = capability.get().codecType; if (codec_type_set.find(codec_type) != codec_type_set.end()) { log::info("enabled offloading capability={}", capability.toString()); offloading_preference.push_back(capability); } else { log::info("disabled offloading capability={}", capability.toString()); } } // TODO: Bluetooth SoC and runtime property return true; } /*** * Check whether this codec is supported by the audio HAL and is allowed to * use by prefernece of framework / Bluetooth SoC / runtime property. ***/ bool IsCodecOffloadingEnabled(const CodecConfiguration& codec_config) { for (auto preference : offloading_preference) { if (codec_config.codecType != preference.get().codecType) { continue; } auto codec_capability = preference.get(); switch (codec_capability.codecType) { case CodecType::SBC: { auto sbc_capability = codec_capability.capabilities .get(); auto sbc_config = codec_config.config .get(); return sbc_offloading_capability_match(sbc_capability, sbc_config); } case CodecType::AAC: { auto aac_capability = codec_capability.capabilities .get(); auto aac_config = codec_config.config .get(); return aac_offloading_capability_match(aac_capability, aac_config); } case CodecType::APTX: [[fallthrough]]; case CodecType::APTX_HD: { auto aptx_capability = codec_capability.capabilities .get(); auto aptx_config = codec_config.config .get(); return aptx_offloading_capability_match(aptx_capability, aptx_config); } case CodecType::LDAC: { auto ldac_capability = codec_capability.capabilities .get(); auto ldac_config = codec_config.config .get(); return ldac_offloading_capability_match(ldac_capability, ldac_config); } case CodecType::OPUS: { std::optional opus_capability = codec_capability.capabilities .get(); std::optional opus_config = codec_config.config .get(); return opus_offloading_capability_match(opus_capability, opus_config); } case CodecType::UNKNOWN: [[fallthrough]]; default: log::error("Unknown codecType={}", toString(codec_capability.codecType)); return false; } } log::info("software codec={}", codec_config.toString()); return false; } } // namespace codec } // namespace aidl } // namespace audio } // namespace bluetooth