You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
572 lines
22 KiB
572 lines
22 KiB
/*
|
|
* Copyright 2019 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 "bluetooth"
|
|
|
|
#include "codec_status.h"
|
|
#include "client_interface.h"
|
|
|
|
#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"
|
|
|
|
namespace {
|
|
|
|
using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
|
|
using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
|
|
using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
|
|
using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
|
|
using ::android::hardware::bluetooth::audio::V2_0::AudioCapabilities;
|
|
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
|
|
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
|
|
using ::android::hardware::bluetooth::audio::V2_0::CodecType;
|
|
using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
|
|
using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
|
|
using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
|
|
using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
|
|
using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
|
|
using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
|
|
using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
|
|
using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
|
|
using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
|
|
|
|
// capabilities from BluetoothAudioClientInterface::GetAudioCapabilities()
|
|
std::vector<AudioCapabilities> audio_hal_capabilities(0);
|
|
// capabilities that audio HAL supports and frameworks / Bluetooth SoC / runtime
|
|
// preference would like to use.
|
|
std::vector<AudioCapabilities> offloading_preference(0);
|
|
|
|
bool sbc_offloading_capability_match(const SbcParameters& sbc_capability,
|
|
const SbcParameters& sbc_config) {
|
|
if ((static_cast<SampleRate>(sbc_capability.sampleRate &
|
|
sbc_config.sampleRate) ==
|
|
SampleRate::RATE_UNKNOWN) ||
|
|
(static_cast<SbcChannelMode>(sbc_capability.channelMode &
|
|
sbc_config.channelMode) ==
|
|
SbcChannelMode::UNKNOWN) ||
|
|
(static_cast<SbcBlockLength>(sbc_capability.blockLength &
|
|
sbc_config.blockLength) ==
|
|
static_cast<SbcBlockLength>(0)) ||
|
|
(static_cast<SbcNumSubbands>(sbc_capability.numSubbands &
|
|
sbc_config.numSubbands) ==
|
|
static_cast<SbcNumSubbands>(0)) ||
|
|
(static_cast<SbcAllocMethod>(sbc_capability.allocMethod &
|
|
sbc_config.allocMethod) ==
|
|
static_cast<SbcAllocMethod>(0)) ||
|
|
(static_cast<BitsPerSample>(sbc_capability.bitsPerSample &
|
|
sbc_config.bitsPerSample) ==
|
|
BitsPerSample::BITS_UNKNOWN) ||
|
|
(sbc_config.minBitpool < sbc_capability.minBitpool ||
|
|
sbc_config.maxBitpool < sbc_config.minBitpool ||
|
|
sbc_capability.maxBitpool < sbc_config.maxBitpool)) {
|
|
LOG(WARNING) << __func__ << ": software codec=" << toString(sbc_config)
|
|
<< " capability=" << toString(sbc_capability);
|
|
return false;
|
|
}
|
|
VLOG(1) << __func__ << ": offloading codec=" << toString(sbc_config)
|
|
<< " capability=" << toString(sbc_capability);
|
|
return true;
|
|
}
|
|
|
|
bool aac_offloading_capability_match(const AacParameters& aac_capability,
|
|
const AacParameters& aac_config) {
|
|
if ((static_cast<AacObjectType>(aac_capability.objectType &
|
|
aac_config.objectType) ==
|
|
static_cast<AacObjectType>(0)) ||
|
|
(static_cast<SampleRate>(aac_capability.sampleRate &
|
|
aac_config.sampleRate) ==
|
|
SampleRate::RATE_UNKNOWN) ||
|
|
(static_cast<ChannelMode>(aac_capability.channelMode &
|
|
aac_config.channelMode) ==
|
|
ChannelMode::UNKNOWN) ||
|
|
(aac_capability.variableBitRateEnabled != AacVariableBitRate::ENABLED &&
|
|
aac_config.variableBitRateEnabled != AacVariableBitRate::DISABLED) ||
|
|
(static_cast<BitsPerSample>(aac_capability.bitsPerSample &
|
|
aac_config.bitsPerSample) ==
|
|
BitsPerSample::BITS_UNKNOWN)) {
|
|
LOG(WARNING) << __func__ << ": software codec=" << toString(aac_config)
|
|
<< " capability=" << toString(aac_capability);
|
|
return false;
|
|
}
|
|
VLOG(1) << __func__ << ": offloading codec=" << toString(aac_config)
|
|
<< " capability=" << toString(aac_capability);
|
|
return true;
|
|
}
|
|
|
|
bool aptx_offloading_capability_match(const AptxParameters& aptx_capability,
|
|
const AptxParameters& aptx_config) {
|
|
if ((static_cast<SampleRate>(aptx_capability.sampleRate &
|
|
aptx_config.sampleRate) ==
|
|
SampleRate::RATE_UNKNOWN) ||
|
|
(static_cast<ChannelMode>(aptx_capability.channelMode &
|
|
aptx_config.channelMode) ==
|
|
ChannelMode::UNKNOWN) ||
|
|
(static_cast<BitsPerSample>(aptx_capability.bitsPerSample &
|
|
aptx_config.bitsPerSample) ==
|
|
BitsPerSample::BITS_UNKNOWN)) {
|
|
LOG(WARNING) << __func__ << ": software codec=" << toString(aptx_config)
|
|
<< " capability=" << toString(aptx_capability);
|
|
return false;
|
|
}
|
|
VLOG(1) << __func__ << ": offloading codec=" << toString(aptx_config)
|
|
<< " capability=" << toString(aptx_capability);
|
|
return true;
|
|
}
|
|
|
|
bool ldac_offloading_capability_match(const LdacParameters& ldac_capability,
|
|
const LdacParameters& ldac_config) {
|
|
if ((static_cast<SampleRate>(ldac_capability.sampleRate &
|
|
ldac_config.sampleRate) ==
|
|
SampleRate::RATE_UNKNOWN) ||
|
|
(static_cast<LdacChannelMode>(ldac_capability.channelMode &
|
|
ldac_config.channelMode) ==
|
|
LdacChannelMode::UNKNOWN) ||
|
|
(static_cast<BitsPerSample>(ldac_capability.bitsPerSample &
|
|
ldac_config.bitsPerSample) ==
|
|
BitsPerSample::BITS_UNKNOWN)) {
|
|
LOG(WARNING) << __func__ << ": software codec=" << toString(ldac_config)
|
|
<< " capability=" << toString(ldac_capability);
|
|
return false;
|
|
}
|
|
VLOG(1) << __func__ << ": offloading codec=" << toString(ldac_config)
|
|
<< " capability=" << toString(ldac_capability);
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
namespace bluetooth {
|
|
namespace audio {
|
|
namespace codec {
|
|
|
|
const CodecConfiguration kInvalidCodecConfiguration = {
|
|
.codecType = CodecType::UNKNOWN,
|
|
.encodedAudioBitrate = 0x00000000,
|
|
.peerMtu = 0xffff,
|
|
.isScmstEnabled = false,
|
|
.config = {}};
|
|
|
|
SampleRate A2dpCodecToHalSampleRate(
|
|
const btav_a2dp_codec_config_t& a2dp_codec_config) {
|
|
switch (a2dp_codec_config.sample_rate) {
|
|
case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
|
|
return SampleRate::RATE_44100;
|
|
case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
|
|
return SampleRate::RATE_48000;
|
|
case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
|
|
return SampleRate::RATE_88200;
|
|
case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
|
|
return SampleRate::RATE_96000;
|
|
case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
|
|
return SampleRate::RATE_176400;
|
|
case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
|
|
return SampleRate::RATE_192000;
|
|
case BTAV_A2DP_CODEC_SAMPLE_RATE_16000:
|
|
return SampleRate::RATE_16000;
|
|
case BTAV_A2DP_CODEC_SAMPLE_RATE_24000:
|
|
return SampleRate::RATE_24000;
|
|
default:
|
|
return SampleRate::RATE_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
BitsPerSample 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 BitsPerSample::BITS_16;
|
|
case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
|
|
return BitsPerSample::BITS_24;
|
|
case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
|
|
return BitsPerSample::BITS_32;
|
|
default:
|
|
return BitsPerSample::BITS_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
*codec_config = {};
|
|
return false;
|
|
}
|
|
tBT_A2DP_OFFLOAD a2dp_offload;
|
|
a2dp_config->getCodecSpecificConfig(&a2dp_offload);
|
|
codec_config->codecType = CodecType::SBC;
|
|
codec_config->config.sbcConfig({});
|
|
auto sbc_config = codec_config->config.sbcConfig();
|
|
sbc_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
|
|
if (sbc_config.sampleRate == SampleRate::RATE_UNKNOWN) {
|
|
LOG(ERROR) << __func__
|
|
<< ": 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) << __func__ << ": 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 = SbcBlockLength::BLOCKS_4;
|
|
break;
|
|
case A2DP_SBC_IE_BLOCKS_8:
|
|
sbc_config.blockLength = SbcBlockLength::BLOCKS_8;
|
|
break;
|
|
case A2DP_SBC_IE_BLOCKS_12:
|
|
sbc_config.blockLength = SbcBlockLength::BLOCKS_12;
|
|
break;
|
|
case A2DP_SBC_IE_BLOCKS_16:
|
|
sbc_config.blockLength = SbcBlockLength::BLOCKS_16;
|
|
break;
|
|
default:
|
|
LOG(ERROR) << __func__ << ": 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 = SbcNumSubbands::SUBBAND_4;
|
|
break;
|
|
case A2DP_SBC_IE_SUBBAND_8:
|
|
sbc_config.numSubbands = SbcNumSubbands::SUBBAND_8;
|
|
break;
|
|
default:
|
|
LOG(ERROR) << __func__ << ": 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) << __func__ << ": 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 == BitsPerSample::BITS_UNKNOWN) {
|
|
LOG(ERROR) << __func__ << ": Unknown SBC bits_per_sample="
|
|
<< current_codec.bits_per_sample;
|
|
return false;
|
|
}
|
|
codec_config->config.sbcConfig(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) {
|
|
*codec_config = {};
|
|
return false;
|
|
}
|
|
tBT_A2DP_OFFLOAD a2dp_offload;
|
|
a2dp_config->getCodecSpecificConfig(&a2dp_offload);
|
|
codec_config->codecType = CodecType::AAC;
|
|
codec_config->config.aacConfig({});
|
|
auto aac_config = codec_config->config.aacConfig();
|
|
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) << __func__ << ": Unknown AAC object_type=" << +object_type;
|
|
return false;
|
|
}
|
|
aac_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
|
|
if (aac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Unknown AAC sample_rate=" << current_codec.sample_rate;
|
|
return false;
|
|
}
|
|
aac_config.channelMode = A2dpCodecToHalChannelMode(current_codec);
|
|
if (aac_config.channelMode == ChannelMode::UNKNOWN) {
|
|
LOG(ERROR) << __func__
|
|
<< ": 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 = AacVariableBitRate::ENABLED;
|
|
break;
|
|
case A2DP_AAC_VARIABLE_BIT_RATE_DISABLED:
|
|
aac_config.variableBitRateEnabled = AacVariableBitRate::DISABLED;
|
|
break;
|
|
default:
|
|
LOG(ERROR) << __func__ << ": Unknown AAC VBR=" << +vbr_enabled;
|
|
return false;
|
|
}
|
|
aac_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
|
|
if (aac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
|
|
LOG(ERROR) << __func__ << ": Unknown AAC bits_per_sample="
|
|
<< current_codec.bits_per_sample;
|
|
return false;
|
|
}
|
|
codec_config->config.aacConfig(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) {
|
|
*codec_config = {};
|
|
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;
|
|
}
|
|
codec_config->config.aptxConfig({});
|
|
auto aptx_config = codec_config->config.aptxConfig();
|
|
aptx_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
|
|
if (aptx_config.sampleRate == SampleRate::RATE_UNKNOWN) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Unknown aptX sample_rate=" << current_codec.sample_rate;
|
|
return false;
|
|
}
|
|
aptx_config.channelMode = A2dpCodecToHalChannelMode(current_codec);
|
|
if (aptx_config.channelMode == ChannelMode::UNKNOWN) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Unknown aptX channel_mode=" << current_codec.channel_mode;
|
|
return false;
|
|
}
|
|
aptx_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
|
|
if (aptx_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
|
|
LOG(ERROR) << __func__ << ": Unknown aptX bits_per_sample="
|
|
<< current_codec.bits_per_sample;
|
|
return false;
|
|
}
|
|
codec_config->config.aptxConfig(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) {
|
|
codec_config = {};
|
|
return false;
|
|
}
|
|
tBT_A2DP_OFFLOAD a2dp_offload;
|
|
a2dp_config->getCodecSpecificConfig(&a2dp_offload);
|
|
codec_config->codecType = CodecType::LDAC;
|
|
codec_config->config.ldacConfig({});
|
|
auto ldac_config = codec_config->config.ldacConfig();
|
|
ldac_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
|
|
if (ldac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
|
|
LOG(ERROR) << __func__
|
|
<< ": 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) << __func__ << ": 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::QUALITY_HIGH;
|
|
break;
|
|
case A2DP_LDAC_QUALITY_MID:
|
|
ldac_config.qualityIndex = LdacQualityIndex::QUALITY_MID;
|
|
break;
|
|
case A2DP_LDAC_QUALITY_LOW:
|
|
ldac_config.qualityIndex = LdacQualityIndex::QUALITY_LOW;
|
|
break;
|
|
case A2DP_LDAC_QUALITY_ABR_OFFLOAD:
|
|
ldac_config.qualityIndex = LdacQualityIndex::QUALITY_ABR;
|
|
break;
|
|
default:
|
|
LOG(ERROR) << __func__ << ": Unknown LDAC QualityIndex="
|
|
<< a2dp_offload.codec_info[6];
|
|
return false;
|
|
}
|
|
ldac_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
|
|
if (ldac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
|
|
LOG(ERROR) << __func__ << ": Unknown LDAC bits_per_sample="
|
|
<< current_codec.bits_per_sample;
|
|
return false;
|
|
}
|
|
codec_config->config.ldacConfig(ldac_config);
|
|
return true;
|
|
}
|
|
|
|
bool UpdateOffloadingCapabilities(
|
|
const std::vector<btav_a2dp_codec_config_t>& framework_preference) {
|
|
audio_hal_capabilities = BluetoothAudioClientInterface::GetAudioCapabilities(
|
|
SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
|
|
uint32_t codec_type_masks = static_cast<uint32_t>(CodecType::UNKNOWN);
|
|
for (auto preference : framework_preference) {
|
|
switch (preference.codec_type) {
|
|
case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
|
|
codec_type_masks |= CodecType::SBC;
|
|
break;
|
|
case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
|
|
codec_type_masks |= CodecType::AAC;
|
|
break;
|
|
case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
|
|
codec_type_masks |= CodecType::APTX;
|
|
break;
|
|
case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
|
|
codec_type_masks |= CodecType::APTX_HD;
|
|
break;
|
|
case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC:
|
|
codec_type_masks |= CodecType::LDAC;
|
|
break;
|
|
case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
|
|
[[fallthrough]];
|
|
case BTAV_A2DP_CODEC_INDEX_SINK_AAC:
|
|
[[fallthrough]];
|
|
case BTAV_A2DP_CODEC_INDEX_SINK_LDAC:
|
|
LOG(WARNING) << __func__
|
|
<< ": Ignore sink codec_type=" << preference.codec_type;
|
|
break;
|
|
case BTAV_A2DP_CODEC_INDEX_MAX:
|
|
[[fallthrough]];
|
|
default:
|
|
LOG(ERROR) << __func__
|
|
<< ": Unknown codec_type=" << preference.codec_type;
|
|
return false;
|
|
}
|
|
}
|
|
offloading_preference.clear();
|
|
for (auto capability : audio_hal_capabilities) {
|
|
if (static_cast<CodecType>(capability.codecCapabilities().codecType &
|
|
codec_type_masks) != CodecType::UNKNOWN) {
|
|
LOG(INFO) << __func__
|
|
<< ": enabled offloading capability=" << toString(capability);
|
|
offloading_preference.push_back(capability);
|
|
} else {
|
|
LOG(INFO) << __func__
|
|
<< ": disabled offloading capability=" << toString(capability);
|
|
}
|
|
}
|
|
// 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.codecCapabilities().codecType)
|
|
continue;
|
|
auto codec_capability = preference.codecCapabilities();
|
|
switch (codec_capability.codecType) {
|
|
case CodecType::SBC: {
|
|
auto sbc_capability = codec_capability.capabilities.sbcCapabilities();
|
|
auto sbc_config = codec_config.config.sbcConfig();
|
|
return sbc_offloading_capability_match(sbc_capability, sbc_config);
|
|
}
|
|
case CodecType::AAC: {
|
|
auto aac_capability = codec_capability.capabilities.aacCapabilities();
|
|
auto aac_config = codec_config.config.aacConfig();
|
|
return aac_offloading_capability_match(aac_capability, aac_config);
|
|
}
|
|
case CodecType::APTX:
|
|
[[fallthrough]];
|
|
case CodecType::APTX_HD: {
|
|
auto aptx_capability = codec_capability.capabilities.aptxCapabilities();
|
|
auto aptx_config = codec_config.config.aptxConfig();
|
|
return aptx_offloading_capability_match(aptx_capability, aptx_config);
|
|
}
|
|
case CodecType::LDAC: {
|
|
auto ldac_capability = codec_capability.capabilities.ldacCapabilities();
|
|
auto ldac_config = codec_config.config.ldacConfig();
|
|
return ldac_offloading_capability_match(ldac_capability, ldac_config);
|
|
}
|
|
case CodecType::UNKNOWN:
|
|
[[fallthrough]];
|
|
default:
|
|
LOG(ERROR) << __func__ << ": Unknown codecType="
|
|
<< toString(codec_capability.codecType);
|
|
return false;
|
|
}
|
|
}
|
|
LOG(INFO) << __func__ << ": software codec=" << toString(codec_config);
|
|
return false;
|
|
}
|
|
|
|
} // namespace codec
|
|
} // namespace audio
|
|
} // namespace bluetooth
|