Merge changes I15075455,I03f322e8 into main am: 1cb6465c10
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/3109159 Change-Id: I970647e0bdffd9a6f1ebf42c448298fae02bf053 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
aa1b56973e
5 changed files with 630 additions and 161 deletions
|
@ -30,6 +30,8 @@ namespace audio {
|
||||||
|
|
||||||
constexpr uint8_t kLeAudioDirectionSink = 0x01;
|
constexpr uint8_t kLeAudioDirectionSink = 0x01;
|
||||||
constexpr uint8_t kLeAudioDirectionSource = 0x02;
|
constexpr uint8_t kLeAudioDirectionSource = 0x02;
|
||||||
|
constexpr uint8_t kIsoDataPathHci = 0x00;
|
||||||
|
constexpr uint8_t kIsoDataPathPlatformDefault = 0x01;
|
||||||
|
|
||||||
const std::map<CodecSpecificConfigurationLtv::SamplingFrequency, uint32_t>
|
const std::map<CodecSpecificConfigurationLtv::SamplingFrequency, uint32_t>
|
||||||
freq_to_support_bitmask_map = {
|
freq_to_support_bitmask_map = {
|
||||||
|
@ -85,6 +87,7 @@ const std::map<CodecSpecificConfigurationLtv::FrameDuration, uint32_t>
|
||||||
std::map<int32_t, CodecSpecificConfigurationLtv::SamplingFrequency>
|
std::map<int32_t, CodecSpecificConfigurationLtv::SamplingFrequency>
|
||||||
sampling_freq_map = {
|
sampling_freq_map = {
|
||||||
{16000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000},
|
{16000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000},
|
||||||
|
{24000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000},
|
||||||
{48000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000},
|
{48000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000},
|
||||||
{96000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ96000},
|
{96000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ96000},
|
||||||
};
|
};
|
||||||
|
@ -175,12 +178,13 @@ bool LeAudioOffloadAudioProvider::filterCapabilitiesMatchedContext(
|
||||||
if (!metadata.has_value()) continue;
|
if (!metadata.has_value()) continue;
|
||||||
if (metadata.value().getTag() == MetadataLtv::Tag::preferredAudioContexts) {
|
if (metadata.value().getTag() == MetadataLtv::Tag::preferredAudioContexts) {
|
||||||
// Check all pref audio context to see if anything matched
|
// Check all pref audio context to see if anything matched
|
||||||
auto& context = metadata.value()
|
auto& prefer_context =
|
||||||
.get<MetadataLtv::Tag::preferredAudioContexts>()
|
metadata.value()
|
||||||
.values;
|
.get<MetadataLtv::Tag::preferredAudioContexts>()
|
||||||
if (setting_context.bitmask & context.bitmask) {
|
.values;
|
||||||
|
if (setting_context.bitmask & prefer_context.bitmask) {
|
||||||
// New mask with matched capability
|
// New mask with matched capability
|
||||||
setting_context.bitmask &= context.bitmask;
|
setting_context.bitmask &= prefer_context.bitmask;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,8 +223,9 @@ bool LeAudioOffloadAudioProvider::isMatchedAudioChannel(
|
||||||
/*cfg_channel*/,
|
/*cfg_channel*/,
|
||||||
CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
|
CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
|
||||||
/*capability_channel*/) {
|
/*capability_channel*/) {
|
||||||
|
// Simply ignore.
|
||||||
|
// Later can use additional capabilities to match requirement.
|
||||||
bool isMatched = true;
|
bool isMatched = true;
|
||||||
// TODO: how to match?
|
|
||||||
return isMatched;
|
return isMatched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,22 +322,34 @@ bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedCodecConfiguration(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration(
|
bool isMonoConfig(
|
||||||
LeAudioAseConfiguration setting_cfg,
|
CodecSpecificConfigurationLtv::AudioChannelAllocation allocation) {
|
||||||
LeAudioAseConfiguration requirement_cfg) {
|
auto channel_count = std::bitset<32>(allocation.bitmask);
|
||||||
|
return (channel_count.count() <= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
|
||||||
|
LeAudioAseConfiguration& setting_cfg,
|
||||||
|
const LeAudioAseConfiguration& requirement_cfg) {
|
||||||
// Check matching for codec configuration <=> requirement ASE codec
|
// Check matching for codec configuration <=> requirement ASE codec
|
||||||
// Also match if no CodecId requirement
|
// Also match if no CodecId requirement
|
||||||
if (requirement_cfg.codecId.has_value()) {
|
if (requirement_cfg.codecId.has_value()) {
|
||||||
if (!setting_cfg.codecId.has_value()) return false;
|
if (!setting_cfg.codecId.has_value()) return false;
|
||||||
if (!isMatchedValidCodec(setting_cfg.codecId.value(),
|
if (!isMatchedValidCodec(setting_cfg.codecId.value(),
|
||||||
requirement_cfg.codecId.value())) {
|
requirement_cfg.codecId.value())) {
|
||||||
|
LOG(WARNING) << __func__ << ": Doesn't match valid codec, cfg = "
|
||||||
|
<< setting_cfg.codecId.value().toString()
|
||||||
|
<< ", req = " << requirement_cfg.codecId.value().toString();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requirement_cfg.targetLatency ==
|
if (requirement_cfg.targetLatency !=
|
||||||
LeAudioAseConfiguration::TargetLatency::UNDEFINED ||
|
LeAudioAseConfiguration::TargetLatency::UNDEFINED &&
|
||||||
setting_cfg.targetLatency != requirement_cfg.targetLatency) {
|
setting_cfg.targetLatency != requirement_cfg.targetLatency) {
|
||||||
|
LOG(WARNING) << __func__ << ": Doesn't match target latency, cfg = "
|
||||||
|
<< int(setting_cfg.targetLatency)
|
||||||
|
<< ", req = " << int(requirement_cfg.targetLatency);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Ignore PHY requirement
|
// Ignore PHY requirement
|
||||||
|
@ -346,11 +363,24 @@ bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration(
|
||||||
for (auto requirement_cfg : requirement_cfg.codecConfiguration) {
|
for (auto requirement_cfg : requirement_cfg.codecConfiguration) {
|
||||||
// Directly compare CodecSpecificConfigurationLtv
|
// Directly compare CodecSpecificConfigurationLtv
|
||||||
auto cfg = cfg_tag_map.find(requirement_cfg.getTag());
|
auto cfg = cfg_tag_map.find(requirement_cfg.getTag());
|
||||||
|
// Config not found for this requirement, cannot match
|
||||||
if (cfg == cfg_tag_map.end()) {
|
if (cfg == cfg_tag_map.end()) {
|
||||||
|
LOG(WARNING) << __func__ << ": Config not found for the requirement "
|
||||||
|
<< requirement_cfg.toString();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore matching for audio channel allocation
|
||||||
|
// since the rule is complicated. Match outside instead
|
||||||
|
if (requirement_cfg.getTag() ==
|
||||||
|
CodecSpecificConfigurationLtv::Tag::audioChannelAllocation)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (cfg->second != requirement_cfg) {
|
if (cfg->second != requirement_cfg) {
|
||||||
|
LOG(WARNING) << __func__
|
||||||
|
<< ": Config doesn't match the requirement, cfg = "
|
||||||
|
<< cfg->second.toString()
|
||||||
|
<< ", req = " << requirement_cfg.toString();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,35 +425,133 @@ void LeAudioOffloadAudioProvider::filterCapabilitiesAseDirectionConfiguration(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getLeAudioAseConfigurationAllocationBitmask(LeAudioAseConfiguration cfg) {
|
||||||
|
for (auto cfg_ltv : cfg.codecConfiguration) {
|
||||||
|
if (cfg_ltv.getTag() ==
|
||||||
|
CodecSpecificConfigurationLtv::Tag::audioChannelAllocation) {
|
||||||
|
return cfg_ltv
|
||||||
|
.get<CodecSpecificConfigurationLtv::Tag::audioChannelAllocation>()
|
||||||
|
.bitmask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCountFromBitmask(int bitmask) {
|
||||||
|
return std::bitset<32>(bitmask).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AseDirectionConfiguration> findValidMonoConfig(
|
||||||
|
std::vector<AseDirectionConfiguration>& valid_direction_configurations,
|
||||||
|
int bitmask) {
|
||||||
|
for (auto& cfg : valid_direction_configurations) {
|
||||||
|
int cfg_bitmask =
|
||||||
|
getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration);
|
||||||
|
if (getCountFromBitmask(cfg_bitmask) <= 1) {
|
||||||
|
// Modify the bitmask to be the same as the requirement
|
||||||
|
for (auto& codec_cfg : cfg.aseConfiguration.codecConfiguration) {
|
||||||
|
if (codec_cfg.getTag() ==
|
||||||
|
CodecSpecificConfigurationLtv::Tag::audioChannelAllocation) {
|
||||||
|
codec_cfg
|
||||||
|
.get<CodecSpecificConfigurationLtv::Tag::audioChannelAllocation>()
|
||||||
|
.bitmask = bitmask;
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<AseDirectionConfiguration> getValidConfigurationsFromAllocation(
|
||||||
|
int req_allocation_bitmask,
|
||||||
|
std::vector<AseDirectionConfiguration>& valid_direction_configurations,
|
||||||
|
bool is_exact) {
|
||||||
|
// Prefer the same allocation_bitmask
|
||||||
|
int channel_count = getCountFromBitmask(req_allocation_bitmask);
|
||||||
|
|
||||||
|
if (is_exact) {
|
||||||
|
for (auto& cfg : valid_direction_configurations) {
|
||||||
|
int cfg_bitmask =
|
||||||
|
getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration);
|
||||||
|
if (cfg_bitmask == req_allocation_bitmask) {
|
||||||
|
LOG(DEBUG)
|
||||||
|
<< __func__
|
||||||
|
<< ": Found an exact match for the requirement allocation of "
|
||||||
|
<< cfg_bitmask;
|
||||||
|
return {cfg};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
// Not using exact match strategy
|
||||||
|
if (channel_count <= 1) {
|
||||||
|
// Mono requirement matched if cfg is a mono config
|
||||||
|
auto cfg = findValidMonoConfig(valid_direction_configurations,
|
||||||
|
req_allocation_bitmask);
|
||||||
|
if (cfg.has_value()) return {cfg.value()};
|
||||||
|
} else {
|
||||||
|
// Stereo requirement returns 2 mono configs
|
||||||
|
// that has a combined bitmask equal to the stereo config
|
||||||
|
std::vector<AseDirectionConfiguration> temp;
|
||||||
|
for (int bit = 0; bit < 32; ++bit)
|
||||||
|
if (req_allocation_bitmask & (1 << bit)) {
|
||||||
|
auto cfg =
|
||||||
|
findValidMonoConfig(valid_direction_configurations, (1 << bit));
|
||||||
|
if (cfg.has_value()) temp.push_back(cfg.value());
|
||||||
|
}
|
||||||
|
if (temp.size() == channel_count) return temp;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
|
void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
|
||||||
std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
|
std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
|
||||||
direction_configurations,
|
direction_configurations,
|
||||||
const std::vector<std::optional<AseDirectionRequirement>>& requirements,
|
const std::vector<std::optional<AseDirectionRequirement>>& requirements,
|
||||||
std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
|
std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
|
||||||
valid_direction_configurations) {
|
valid_direction_configurations,
|
||||||
|
bool is_exact) {
|
||||||
|
// For every requirement, find the matched ase configuration
|
||||||
|
if (!direction_configurations.has_value()) return;
|
||||||
|
|
||||||
if (!valid_direction_configurations.has_value()) {
|
if (!valid_direction_configurations.has_value()) {
|
||||||
valid_direction_configurations =
|
valid_direction_configurations =
|
||||||
std::vector<std::optional<AseDirectionConfiguration>>();
|
std::vector<std::optional<AseDirectionConfiguration>>();
|
||||||
}
|
}
|
||||||
// For every requirement, find the matched ase configuration
|
|
||||||
if (!direction_configurations.has_value()) return;
|
|
||||||
for (auto& requirement : requirements) {
|
for (auto& requirement : requirements) {
|
||||||
if (!requirement.has_value()) continue;
|
if (!requirement.has_value()) continue;
|
||||||
|
auto req_allocation_bitmask = getLeAudioAseConfigurationAllocationBitmask(
|
||||||
|
requirement.value().aseConfiguration);
|
||||||
|
auto req_channel_count = getCountFromBitmask(req_allocation_bitmask);
|
||||||
|
|
||||||
|
auto temp = std::vector<AseDirectionConfiguration>();
|
||||||
|
|
||||||
for (auto direction_configuration : direction_configurations.value()) {
|
for (auto direction_configuration : direction_configurations.value()) {
|
||||||
if (!direction_configuration.has_value()) continue;
|
if (!direction_configuration.has_value()) continue;
|
||||||
if (!isMatchedAseConfiguration(
|
if (!filterMatchedAseConfiguration(
|
||||||
direction_configuration.value().aseConfiguration,
|
direction_configuration.value().aseConfiguration,
|
||||||
requirement.value().aseConfiguration))
|
requirement.value().aseConfiguration))
|
||||||
continue;
|
continue;
|
||||||
// Valid if match any requirement.
|
// Valid if match any requirement.
|
||||||
valid_direction_configurations.value().push_back(direction_configuration);
|
temp.push_back(direction_configuration.value());
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
// Get the best matching config based on channel allocation
|
||||||
|
auto total_cfg_channel_count = 0;
|
||||||
|
auto req_valid_configs = getValidConfigurationsFromAllocation(
|
||||||
|
req_allocation_bitmask, temp, is_exact);
|
||||||
|
// Count and check required channel counts
|
||||||
|
for (auto& cfg : req_valid_configs) {
|
||||||
|
total_cfg_channel_count += getCountFromBitmask(
|
||||||
|
getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration));
|
||||||
|
valid_direction_configurations.value().push_back(cfg);
|
||||||
|
}
|
||||||
|
if (total_cfg_channel_count != req_channel_count) {
|
||||||
|
valid_direction_configurations = std::nullopt;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Ensure that each requirement will have one direction configurations
|
|
||||||
if (valid_direction_configurations.value().empty() ||
|
|
||||||
(valid_direction_configurations.value().size() != requirements.size())) {
|
|
||||||
valid_direction_configurations = std::nullopt;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,10 +572,6 @@ LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings(
|
||||||
.flags = setting.flags,
|
.flags = setting.flags,
|
||||||
.packing = setting.packing,
|
.packing = setting.packing,
|
||||||
};
|
};
|
||||||
// Try to match context in metadata.
|
|
||||||
if (!filterCapabilitiesMatchedContext(filtered_setting.audioContext,
|
|
||||||
capabilities))
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
// Get a list of all matched AseDirectionConfiguration
|
// Get a list of all matched AseDirectionConfiguration
|
||||||
// for the input direction
|
// for the input direction
|
||||||
|
@ -488,8 +612,8 @@ LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings(
|
||||||
std::optional<LeAudioAseConfigurationSetting>
|
std::optional<LeAudioAseConfigurationSetting>
|
||||||
LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
|
LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
|
||||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
||||||
const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
|
const IBluetoothAudioProvider::LeAudioConfigurationRequirement& requirement,
|
||||||
requirement) {
|
bool is_exact) {
|
||||||
// Try to match context in metadata.
|
// Try to match context in metadata.
|
||||||
if ((setting.audioContext.bitmask & requirement.audioContext.bitmask) !=
|
if ((setting.audioContext.bitmask & requirement.audioContext.bitmask) !=
|
||||||
requirement.audioContext.bitmask)
|
requirement.audioContext.bitmask)
|
||||||
|
@ -498,12 +622,6 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
|
||||||
// Further filter setting's context
|
// Further filter setting's context
|
||||||
setting.audioContext.bitmask &= requirement.audioContext.bitmask;
|
setting.audioContext.bitmask &= requirement.audioContext.bitmask;
|
||||||
|
|
||||||
// Check requirement for the correct direction
|
|
||||||
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>*
|
|
||||||
direction_requirement;
|
|
||||||
std::vector<std::optional<AseDirectionConfiguration>>*
|
|
||||||
direction_configuration;
|
|
||||||
|
|
||||||
// Create a new LeAudioAseConfigurationSetting to return
|
// Create a new LeAudioAseConfigurationSetting to return
|
||||||
LeAudioAseConfigurationSetting filtered_setting{
|
LeAudioAseConfigurationSetting filtered_setting{
|
||||||
.audioContext = setting.audioContext,
|
.audioContext = setting.audioContext,
|
||||||
|
@ -514,22 +632,63 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
|
||||||
if (requirement.sinkAseRequirement.has_value()) {
|
if (requirement.sinkAseRequirement.has_value()) {
|
||||||
filterRequirementAseDirectionConfiguration(
|
filterRequirementAseDirectionConfiguration(
|
||||||
setting.sinkAseConfiguration, requirement.sinkAseRequirement.value(),
|
setting.sinkAseConfiguration, requirement.sinkAseRequirement.value(),
|
||||||
filtered_setting.sinkAseConfiguration);
|
filtered_setting.sinkAseConfiguration, is_exact);
|
||||||
if (!filtered_setting.sinkAseConfiguration.has_value()) return std::nullopt;
|
if (!filtered_setting.sinkAseConfiguration.has_value()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requirement.sourceAseRequirement.has_value()) {
|
if (requirement.sourceAseRequirement.has_value()) {
|
||||||
filterRequirementAseDirectionConfiguration(
|
filterRequirementAseDirectionConfiguration(
|
||||||
setting.sourceAseConfiguration,
|
setting.sourceAseConfiguration,
|
||||||
requirement.sourceAseRequirement.value(),
|
requirement.sourceAseRequirement.value(),
|
||||||
filtered_setting.sourceAseConfiguration);
|
filtered_setting.sourceAseConfiguration, is_exact);
|
||||||
if (!filtered_setting.sourceAseConfiguration.has_value())
|
if (!filtered_setting.sourceAseConfiguration.has_value()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filtered_setting;
|
return filtered_setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
|
||||||
|
LeAudioOffloadAudioProvider::matchWithRequirement(
|
||||||
|
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>&
|
||||||
|
matched_ase_configuration_settings,
|
||||||
|
const std::vector<IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
|
||||||
|
in_requirements,
|
||||||
|
bool is_exact) {
|
||||||
|
// Each requirement will match with a valid setting
|
||||||
|
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
|
||||||
|
for (auto& requirement : in_requirements) {
|
||||||
|
LOG(INFO) << __func__ << ": Trying to match for the requirement "
|
||||||
|
<< requirement.toString();
|
||||||
|
bool is_matched = false;
|
||||||
|
|
||||||
|
for (auto& setting : matched_ase_configuration_settings) {
|
||||||
|
auto filtered_ase_configuration_setting =
|
||||||
|
getRequirementMatchedAseConfigurationSettings(setting, requirement,
|
||||||
|
is_exact);
|
||||||
|
if (filtered_ase_configuration_setting.has_value()) {
|
||||||
|
result.push_back(filtered_ase_configuration_setting.value());
|
||||||
|
LOG(INFO) << __func__ << ": Result = "
|
||||||
|
<< filtered_ase_configuration_setting.value().toString();
|
||||||
|
// Found a matched setting, ignore other settings
|
||||||
|
is_matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!is_matched) {
|
||||||
|
// If cannot satisfy this requirement, return an empty result
|
||||||
|
LOG(WARNING) << __func__ << ": Cannot match the requirement "
|
||||||
|
<< requirement.toString();
|
||||||
|
result.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// For each requirement, a valid ASE configuration will satify:
|
// For each requirement, a valid ASE configuration will satify:
|
||||||
// - matched with any sink capability (if presented)
|
// - matched with any sink capability (if presented)
|
||||||
// - OR matched with any source capability (if presented)
|
// - OR matched with any source capability (if presented)
|
||||||
|
@ -555,31 +714,43 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
|
||||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each setting consist of source and sink AseDirectionConfiguration vector
|
// Split out preferred and non-preferred settings based on context
|
||||||
// Filter every sink capability
|
// An example: preferred = MEDIA, available: MEDIA | CONVERSATION
|
||||||
|
// -> preferred list will have settings with MEDIA context
|
||||||
|
// -> non-preferred list will have settings with any context
|
||||||
|
// We want to match requirement with preferred context settings first
|
||||||
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
|
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
|
||||||
matched_ase_configuration_settings;
|
matched_ase_configuration_settings;
|
||||||
|
// Matched ASE configuration with non-preferred audio context
|
||||||
|
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
|
||||||
|
non_prefer_matched_ase_configuration_settings;
|
||||||
|
|
||||||
if (in_remoteSinkAudioCapabilities.has_value()) {
|
if (in_remoteSinkAudioCapabilities.has_value())
|
||||||
// Matching each setting with any remote capabilities
|
// Matching each setting with any remote capabilities
|
||||||
for (auto& setting : ase_configuration_settings) {
|
for (auto& setting : ase_configuration_settings)
|
||||||
for (auto& capability : in_remoteSinkAudioCapabilities.value()) {
|
for (auto& capability : in_remoteSinkAudioCapabilities.value()) {
|
||||||
if (!capability.has_value()) continue;
|
if (!capability.has_value()) continue;
|
||||||
auto filtered_ase_configuration_setting =
|
auto filtered_ase_configuration_setting =
|
||||||
getCapabilitiesMatchedAseConfigurationSettings(
|
getCapabilitiesMatchedAseConfigurationSettings(
|
||||||
setting, capability.value(), kLeAudioDirectionSink);
|
setting, capability.value(), kLeAudioDirectionSink);
|
||||||
if (filtered_ase_configuration_setting.has_value()) {
|
if (filtered_ase_configuration_setting.has_value()) {
|
||||||
matched_ase_configuration_settings.push_back(
|
// Push to non-prefer first for the broadest matching possible
|
||||||
|
non_prefer_matched_ase_configuration_settings.push_back(
|
||||||
filtered_ase_configuration_setting.value());
|
filtered_ase_configuration_setting.value());
|
||||||
|
// Try to filter out prefer context to another vector.
|
||||||
|
if (filterCapabilitiesMatchedContext(
|
||||||
|
filtered_ase_configuration_setting.value().audioContext,
|
||||||
|
capability.value())) {
|
||||||
|
matched_ase_configuration_settings.push_back(
|
||||||
|
filtered_ase_configuration_setting.value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combine filter every source capability
|
// Combine filter every source capability
|
||||||
if (in_remoteSourceAudioCapabilities.has_value()) {
|
if (in_remoteSourceAudioCapabilities.has_value())
|
||||||
// Matching each setting with any remote capabilities
|
// Matching each setting with any remote capabilities
|
||||||
for (auto& setting : ase_configuration_settings) {
|
for (auto& setting : ase_configuration_settings)
|
||||||
for (auto& capability : in_remoteSourceAudioCapabilities.value()) {
|
for (auto& capability : in_remoteSourceAudioCapabilities.value()) {
|
||||||
if (!capability.has_value()) continue;
|
if (!capability.has_value()) continue;
|
||||||
auto filtered_ase_configuration_setting =
|
auto filtered_ase_configuration_setting =
|
||||||
|
@ -588,45 +759,56 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
|
||||||
if (filtered_ase_configuration_setting.has_value()) {
|
if (filtered_ase_configuration_setting.has_value()) {
|
||||||
// Put into the same list
|
// Put into the same list
|
||||||
// possibly duplicated, filtered by requirement later
|
// possibly duplicated, filtered by requirement later
|
||||||
matched_ase_configuration_settings.push_back(
|
// Push to non-prefer first for the broadest matching possible
|
||||||
|
non_prefer_matched_ase_configuration_settings.push_back(
|
||||||
filtered_ase_configuration_setting.value());
|
filtered_ase_configuration_setting.value());
|
||||||
|
// Try to filter out prefer context to another vector.
|
||||||
|
if (filterCapabilitiesMatchedContext(
|
||||||
|
filtered_ase_configuration_setting.value().audioContext,
|
||||||
|
capability.value())) {
|
||||||
|
matched_ase_configuration_settings.push_back(
|
||||||
|
filtered_ase_configuration_setting.value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matched_ase_configuration_settings.empty()) {
|
// Matching priority list:
|
||||||
LOG(WARNING) << __func__ << ": No setting matched the capability";
|
// Preferred context - exact match with allocation
|
||||||
return ndk::ScopedAStatus::ok();
|
// Any context - exact match with allocation
|
||||||
}
|
// Preferred context - loose match with allocation
|
||||||
// Each requirement will match with a valid setting
|
// Any context - loose match with allocation
|
||||||
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
|
|
||||||
for (auto& requirement : in_requirements) {
|
|
||||||
LOG(INFO) << __func__ << ": Trying to match for the requirement "
|
|
||||||
<< requirement.toString();
|
|
||||||
bool is_matched = false;
|
|
||||||
|
|
||||||
for (auto& setting : matched_ase_configuration_settings) {
|
// A loose match will attempt to return 2 settings with the
|
||||||
auto filtered_ase_configuration_setting =
|
// combined allocation bitmask equal the required allocation.
|
||||||
getRequirementMatchedAseConfigurationSettings(setting, requirement);
|
// For example, we can return 2 link (left link and right link) when
|
||||||
if (filtered_ase_configuration_setting.has_value()) {
|
// the requirement required 1 (left + right) link.
|
||||||
result.push_back(filtered_ase_configuration_setting.value());
|
auto result = matchWithRequirement(matched_ase_configuration_settings,
|
||||||
LOG(INFO) << __func__ << ": Result = "
|
in_requirements, true);
|
||||||
<< filtered_ase_configuration_setting.value().toString();
|
if (result.empty()) {
|
||||||
// Found a matched setting, ignore other settings
|
LOG(WARNING)
|
||||||
is_matched = true;
|
<< __func__
|
||||||
break;
|
<< ": Cannot match with preferred context settings - exact match";
|
||||||
}
|
result = matchWithRequirement(non_prefer_matched_ase_configuration_settings,
|
||||||
}
|
in_requirements, true);
|
||||||
if (!is_matched) {
|
|
||||||
// If cannot satisfy this requirement, return an empty result
|
|
||||||
LOG(WARNING) << __func__ << ": Cannot match the requirement "
|
|
||||||
<< requirement.toString();
|
|
||||||
result.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (result.empty()) {
|
||||||
|
LOG(WARNING)
|
||||||
|
<< __func__
|
||||||
|
<< ": Cannot match with non-preferred context settings - exact match";
|
||||||
|
result = matchWithRequirement(matched_ase_configuration_settings,
|
||||||
|
in_requirements, false);
|
||||||
|
}
|
||||||
|
if (result.empty()) {
|
||||||
|
LOG(WARNING) << __func__
|
||||||
|
<< ": Cannot match with preferred context settings - "
|
||||||
|
"non-exact match";
|
||||||
|
result = matchWithRequirement(non_prefer_matched_ase_configuration_settings,
|
||||||
|
in_requirements, false);
|
||||||
|
}
|
||||||
|
if (result.empty())
|
||||||
|
LOG(ERROR) << __func__
|
||||||
|
<< ": Cannot match with non preferred context settings - "
|
||||||
|
"non-exact match";
|
||||||
*_aidl_return = result;
|
*_aidl_return = result;
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
};
|
};
|
||||||
|
@ -642,7 +824,6 @@ bool LeAudioOffloadAudioProvider::isMatchedQosRequirement(
|
||||||
requirement_qos.maxTransportLatencyMs) {
|
requirement_qos.maxTransportLatencyMs) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Ignore other parameters, as they are not populated in the setting_qos
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,7 +839,8 @@ LeAudioOffloadAudioProvider::getDirectionQosConfiguration(
|
||||||
uint8_t direction,
|
uint8_t direction,
|
||||||
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
|
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
|
||||||
qosRequirement,
|
qosRequirement,
|
||||||
std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings) {
|
std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings,
|
||||||
|
bool is_exact) {
|
||||||
std::optional<AseQosDirectionRequirement> direction_qos_requirement =
|
std::optional<AseQosDirectionRequirement> direction_qos_requirement =
|
||||||
std::nullopt;
|
std::nullopt;
|
||||||
|
|
||||||
|
@ -680,17 +862,26 @@ LeAudioOffloadAudioProvider::getDirectionQosConfiguration(
|
||||||
|
|
||||||
// Get a list of all matched AseDirectionConfiguration
|
// Get a list of all matched AseDirectionConfiguration
|
||||||
// for the input direction
|
// for the input direction
|
||||||
std::vector<std::optional<AseDirectionConfiguration>>*
|
std::optional<std::vector<std::optional<AseDirectionConfiguration>>>
|
||||||
direction_configuration = nullptr;
|
direction_configuration = std::nullopt;
|
||||||
if (direction == kLeAudioDirectionSink) {
|
if (direction == kLeAudioDirectionSink) {
|
||||||
if (!setting.sinkAseConfiguration.has_value()) continue;
|
if (!setting.sinkAseConfiguration.has_value()) continue;
|
||||||
direction_configuration = &setting.sinkAseConfiguration.value();
|
direction_configuration.emplace(setting.sinkAseConfiguration.value());
|
||||||
} else {
|
} else {
|
||||||
if (!setting.sourceAseConfiguration.has_value()) continue;
|
if (!setting.sourceAseConfiguration.has_value()) continue;
|
||||||
direction_configuration = &setting.sourceAseConfiguration.value();
|
direction_configuration.emplace(setting.sourceAseConfiguration.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto cfg : *direction_configuration) {
|
if (!direction_configuration.has_value()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all valid cfg into a vector
|
||||||
|
// Then try to get the best match for audio allocation
|
||||||
|
|
||||||
|
auto temp = std::vector<AseDirectionConfiguration>();
|
||||||
|
|
||||||
|
for (auto& cfg : direction_configuration.value()) {
|
||||||
if (!cfg.has_value()) continue;
|
if (!cfg.has_value()) continue;
|
||||||
// If no requirement, return the first QoS
|
// If no requirement, return the first QoS
|
||||||
if (!direction_qos_requirement.has_value()) {
|
if (!direction_qos_requirement.has_value()) {
|
||||||
|
@ -701,14 +892,30 @@ LeAudioOffloadAudioProvider::getDirectionQosConfiguration(
|
||||||
// Try to match the ASE configuration
|
// Try to match the ASE configuration
|
||||||
// and QoS with requirement
|
// and QoS with requirement
|
||||||
if (!cfg.value().qosConfiguration.has_value()) continue;
|
if (!cfg.value().qosConfiguration.has_value()) continue;
|
||||||
if (isMatchedAseConfiguration(
|
if (filterMatchedAseConfiguration(
|
||||||
cfg.value().aseConfiguration,
|
cfg.value().aseConfiguration,
|
||||||
direction_qos_requirement.value().aseConfiguration) &&
|
direction_qos_requirement.value().aseConfiguration) &&
|
||||||
isMatchedQosRequirement(cfg.value().qosConfiguration.value(),
|
isMatchedQosRequirement(cfg.value().qosConfiguration.value(),
|
||||||
direction_qos_requirement.value())) {
|
direction_qos_requirement.value())) {
|
||||||
return cfg.value().qosConfiguration;
|
temp.push_back(cfg.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LOG(WARNING) << __func__ << ": Got " << temp.size()
|
||||||
|
<< " configs, start matching allocation";
|
||||||
|
|
||||||
|
int qos_allocation_bitmask = getLeAudioAseConfigurationAllocationBitmask(
|
||||||
|
direction_qos_requirement.value().aseConfiguration);
|
||||||
|
// Get the best matching config based on channel allocation
|
||||||
|
auto req_valid_configs = getValidConfigurationsFromAllocation(
|
||||||
|
qos_allocation_bitmask, temp, is_exact);
|
||||||
|
if (req_valid_configs.empty()) {
|
||||||
|
LOG(WARNING) << __func__
|
||||||
|
<< ": Cannot find matching allocation for bitmask "
|
||||||
|
<< qos_allocation_bitmask;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return req_valid_configs[0].qosConfiguration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -730,15 +937,30 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseQosConfiguration(
|
||||||
if (in_qosRequirement.sinkAseQosRequirement.has_value()) {
|
if (in_qosRequirement.sinkAseQosRequirement.has_value()) {
|
||||||
if (!isValidQosRequirement(in_qosRequirement.sinkAseQosRequirement.value()))
|
if (!isValidQosRequirement(in_qosRequirement.sinkAseQosRequirement.value()))
|
||||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||||
result.sinkQosConfiguration = getDirectionQosConfiguration(
|
{
|
||||||
kLeAudioDirectionSink, in_qosRequirement, ase_configuration_settings);
|
// Try exact match first
|
||||||
|
result.sinkQosConfiguration =
|
||||||
|
getDirectionQosConfiguration(kLeAudioDirectionSink, in_qosRequirement,
|
||||||
|
ase_configuration_settings, true);
|
||||||
|
if (!result.sinkQosConfiguration.has_value()) {
|
||||||
|
result.sinkQosConfiguration = getDirectionQosConfiguration(
|
||||||
|
kLeAudioDirectionSink, in_qosRequirement,
|
||||||
|
ase_configuration_settings, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (in_qosRequirement.sourceAseQosRequirement.has_value()) {
|
if (in_qosRequirement.sourceAseQosRequirement.has_value()) {
|
||||||
if (!isValidQosRequirement(
|
if (!isValidQosRequirement(
|
||||||
in_qosRequirement.sourceAseQosRequirement.value()))
|
in_qosRequirement.sourceAseQosRequirement.value()))
|
||||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||||
result.sourceQosConfiguration = getDirectionQosConfiguration(
|
result.sourceQosConfiguration =
|
||||||
kLeAudioDirectionSource, in_qosRequirement, ase_configuration_settings);
|
getDirectionQosConfiguration(kLeAudioDirectionSource, in_qosRequirement,
|
||||||
|
ase_configuration_settings, true);
|
||||||
|
if (!result.sourceQosConfiguration.has_value()) {
|
||||||
|
result.sourceQosConfiguration = getDirectionQosConfiguration(
|
||||||
|
kLeAudioDirectionSource, in_qosRequirement,
|
||||||
|
ase_configuration_settings, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*_aidl_return = result;
|
*_aidl_return = result;
|
||||||
|
@ -763,28 +985,127 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSourceAseMetadataChanged(
|
||||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
LeAudioBroadcastConfigurationSetting getDefaultBroadcastSetting(
|
||||||
|
int context_bitmask, IBluetoothAudioProvider::BroadcastQuality quality) {
|
||||||
|
LeAudioBroadcastConfigurationSetting setting;
|
||||||
|
setting.retransmitionNum = 4;
|
||||||
|
setting.maxTransportLatencyMs = 60;
|
||||||
|
setting.sduIntervalUs = 10000;
|
||||||
|
setting.maxSduOctets = 40;
|
||||||
|
|
||||||
|
if (quality == IBluetoothAudioProvider::BroadcastQuality::HIGH) {
|
||||||
|
LOG(INFO) << __func__ << ": High quality, returning high quality settings";
|
||||||
|
setting.retransmitionNum = 4;
|
||||||
|
setting.maxTransportLatencyMs = 65;
|
||||||
|
setting.maxSduOctets = 200;
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate other settings base on context
|
||||||
|
// TODO: Populate with better design
|
||||||
|
if (context_bitmask & (AudioContext::LIVE_AUDIO | AudioContext::GAME)) {
|
||||||
|
setting.retransmitionNum = 2;
|
||||||
|
setting.maxTransportLatencyMs = 10;
|
||||||
|
setting.maxSduOctets = 120;
|
||||||
|
} else if (context_bitmask & (AudioContext::INSTRUCTIONAL)) {
|
||||||
|
setting.retransmitionNum = 2;
|
||||||
|
setting.maxTransportLatencyMs = 10;
|
||||||
|
setting.maxSduOctets = 40;
|
||||||
|
} else if (context_bitmask &
|
||||||
|
(AudioContext::SOUND_EFFECTS | AudioContext::UNSPECIFIED)) {
|
||||||
|
setting.retransmitionNum = 4;
|
||||||
|
setting.maxTransportLatencyMs = 60;
|
||||||
|
setting.maxSduOctets = 80;
|
||||||
|
} else if (context_bitmask &
|
||||||
|
(AudioContext::ALERTS | AudioContext::NOTIFICATIONS |
|
||||||
|
AudioContext::EMERGENCY_ALARM)) {
|
||||||
|
setting.retransmitionNum = 4;
|
||||||
|
setting.maxTransportLatencyMs = 60;
|
||||||
|
setting.maxSduOctets = 40;
|
||||||
|
} else if (context_bitmask & AudioContext::MEDIA) {
|
||||||
|
setting.retransmitionNum = 4;
|
||||||
|
setting.maxTransportLatencyMs = 60;
|
||||||
|
setting.maxSduOctets = 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
void modifySubBISConfigAllocation(
|
||||||
|
IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration& sub_bis_cfg,
|
||||||
|
int allocation_bitmask) {
|
||||||
|
for (auto& codec_cfg : sub_bis_cfg.bisConfiguration.codecConfiguration) {
|
||||||
|
if (codec_cfg.getTag() ==
|
||||||
|
CodecSpecificConfigurationLtv::audioChannelAllocation) {
|
||||||
|
codec_cfg.get<CodecSpecificConfigurationLtv::audioChannelAllocation>()
|
||||||
|
.bitmask = allocation_bitmask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void modifySubgroupConfiguration(
|
||||||
|
IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration&
|
||||||
|
subgroup_cfg,
|
||||||
|
int context_bitmask) {
|
||||||
|
// STEREO configs
|
||||||
|
// Split into 2 sub BIS config, each has numBis = 1
|
||||||
|
if (context_bitmask & (AudioContext::LIVE_AUDIO | AudioContext::GAME |
|
||||||
|
AudioContext::SOUND_EFFECTS |
|
||||||
|
AudioContext::UNSPECIFIED | AudioContext::MEDIA)) {
|
||||||
|
if (subgroup_cfg.bisConfigurations.size() == 1)
|
||||||
|
subgroup_cfg.bisConfigurations.push_back(
|
||||||
|
subgroup_cfg.bisConfigurations[0]);
|
||||||
|
|
||||||
|
subgroup_cfg.bisConfigurations[0].numBis = 1;
|
||||||
|
modifySubBISConfigAllocation(
|
||||||
|
subgroup_cfg.bisConfigurations[0],
|
||||||
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT);
|
||||||
|
|
||||||
|
subgroup_cfg.bisConfigurations[1].numBis = 1;
|
||||||
|
modifySubBISConfigAllocation(
|
||||||
|
subgroup_cfg.bisConfigurations[1],
|
||||||
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MONO configs
|
||||||
|
for (auto& sub_bis_cfg : subgroup_cfg.bisConfigurations) {
|
||||||
|
sub_bis_cfg.numBis = 1;
|
||||||
|
modifySubBISConfigAllocation(
|
||||||
|
sub_bis_cfg,
|
||||||
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LeAudioOffloadAudioProvider::getBroadcastSettings() {
|
void LeAudioOffloadAudioProvider::getBroadcastSettings() {
|
||||||
if (!broadcast_settings.empty()) return;
|
if (!broadcast_settings.empty()) return;
|
||||||
|
|
||||||
LOG(INFO) << __func__ << ": Loading broadcast settings from provider info";
|
LOG(INFO) << __func__
|
||||||
|
<< ": Loading basic broadcast settings from provider info";
|
||||||
|
|
||||||
std::vector<CodecInfo> db_codec_info =
|
std::vector<CodecInfo> db_codec_info =
|
||||||
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(
|
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(
|
||||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH);
|
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH);
|
||||||
|
for (auto x : db_codec_info) {
|
||||||
|
LOG(INFO) << __func__ << ": codec info = " << x.toString();
|
||||||
|
}
|
||||||
broadcast_settings.clear();
|
broadcast_settings.clear();
|
||||||
|
|
||||||
// Default value for unmapped fields
|
// Default value population
|
||||||
CodecSpecificConfigurationLtv::AudioChannelAllocation default_allocation;
|
CodecSpecificConfigurationLtv::AudioChannelAllocation default_allocation;
|
||||||
default_allocation.bitmask =
|
default_allocation.bitmask =
|
||||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER;
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER;
|
||||||
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU default_frame;
|
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU default_frame;
|
||||||
default_frame.value = 2;
|
default_frame.value = 1;
|
||||||
|
|
||||||
for (auto& codec_info : db_codec_info) {
|
for (auto& codec_info : db_codec_info) {
|
||||||
if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio)
|
if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio)
|
||||||
continue;
|
continue;
|
||||||
auto& transport = codec_info.transport.get<CodecInfo::Transport::leAudio>();
|
auto& transport = codec_info.transport.get<CodecInfo::Transport::leAudio>();
|
||||||
LeAudioBroadcastConfigurationSetting setting;
|
LeAudioBroadcastConfigurationSetting setting;
|
||||||
|
setting.retransmitionNum = 4;
|
||||||
|
setting.maxTransportLatencyMs = 60;
|
||||||
|
setting.sduIntervalUs = 10000;
|
||||||
|
setting.maxSduOctets = 40;
|
||||||
// Default setting
|
// Default setting
|
||||||
setting.numBis = 1;
|
setting.numBis = 1;
|
||||||
setting.phy = {Phy::TWO_M};
|
setting.phy = {Phy::TWO_M};
|
||||||
|
@ -803,13 +1124,15 @@ void LeAudioOffloadAudioProvider::getBroadcastSettings() {
|
||||||
default_frame,
|
default_frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ignore bis_cfg.metadata
|
||||||
|
|
||||||
// Add information to structure
|
// Add information to structure
|
||||||
IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration sub_bis_cfg;
|
IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration sub_bis_cfg;
|
||||||
sub_bis_cfg.numBis = 2;
|
sub_bis_cfg.numBis = 1;
|
||||||
sub_bis_cfg.bisConfiguration = bis_cfg;
|
sub_bis_cfg.bisConfiguration = bis_cfg;
|
||||||
IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration sub_cfg;
|
IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration sub_cfg;
|
||||||
// Populate the same sub config
|
// Populate the same sub config
|
||||||
sub_cfg.bisConfigurations = {sub_bis_cfg, sub_bis_cfg};
|
sub_cfg.bisConfigurations = {sub_bis_cfg};
|
||||||
setting.subgroupsConfigurations = {sub_cfg};
|
setting.subgroupsConfigurations = {sub_cfg};
|
||||||
|
|
||||||
broadcast_settings.push_back(setting);
|
broadcast_settings.push_back(setting);
|
||||||
|
@ -853,34 +1176,91 @@ LeAudioOffloadAudioProvider::
|
||||||
return filtered_setting;
|
return filtered_setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<CodecSpecificConfigurationLtv> getCodecRequirementBasedOnContext(
|
||||||
|
int context_bitmask, IBluetoothAudioProvider::BroadcastQuality quality) {
|
||||||
|
// Default requirement: lc3_stereo_16_2
|
||||||
|
std::vector<CodecSpecificConfigurationLtv> requirement = {
|
||||||
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
|
||||||
|
CodecSpecificConfigurationLtv::FrameDuration::US10000,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (quality == IBluetoothAudioProvider::BroadcastQuality::HIGH) {
|
||||||
|
LOG(INFO) << __func__
|
||||||
|
<< ": High quality, returning high quality requirement";
|
||||||
|
requirement = {
|
||||||
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000,
|
||||||
|
CodecSpecificConfigurationLtv::FrameDuration::US10000,
|
||||||
|
};
|
||||||
|
return requirement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context_bitmask & (AudioContext::LIVE_AUDIO | AudioContext::GAME)) {
|
||||||
|
// lc3_stereo_24_2_1
|
||||||
|
requirement = {
|
||||||
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000,
|
||||||
|
CodecSpecificConfigurationLtv::FrameDuration::US10000,
|
||||||
|
};
|
||||||
|
} else if (context_bitmask & (AudioContext::INSTRUCTIONAL)) {
|
||||||
|
// lc3_mono_16_2
|
||||||
|
requirement = {
|
||||||
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
|
||||||
|
CodecSpecificConfigurationLtv::FrameDuration::US10000,
|
||||||
|
};
|
||||||
|
} else if (context_bitmask &
|
||||||
|
(AudioContext::SOUND_EFFECTS | AudioContext::UNSPECIFIED)) {
|
||||||
|
// lc3_stereo_16_2
|
||||||
|
requirement = {
|
||||||
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
|
||||||
|
CodecSpecificConfigurationLtv::FrameDuration::US10000,
|
||||||
|
};
|
||||||
|
} else if (context_bitmask &
|
||||||
|
(AudioContext::ALERTS | AudioContext::NOTIFICATIONS |
|
||||||
|
AudioContext::EMERGENCY_ALARM)) {
|
||||||
|
// Default requirement: lc3_stereo_16_2
|
||||||
|
requirement = {
|
||||||
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
|
||||||
|
CodecSpecificConfigurationLtv::FrameDuration::US10000,
|
||||||
|
};
|
||||||
|
} else if (context_bitmask & AudioContext::MEDIA) {
|
||||||
|
// Default requirement: lc3_stereo_16_2
|
||||||
|
// Return the 48k requirement
|
||||||
|
requirement = {
|
||||||
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000,
|
||||||
|
CodecSpecificConfigurationLtv::FrameDuration::US10000,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return requirement;
|
||||||
|
}
|
||||||
|
|
||||||
bool LeAudioOffloadAudioProvider::isSubgroupConfigurationMatchedContext(
|
bool LeAudioOffloadAudioProvider::isSubgroupConfigurationMatchedContext(
|
||||||
AudioContext setting_context,
|
AudioContext requirement_context,
|
||||||
|
IBluetoothAudioProvider::BroadcastQuality quality,
|
||||||
LeAudioBroadcastSubgroupConfiguration configuration) {
|
LeAudioBroadcastSubgroupConfiguration configuration) {
|
||||||
// Find any valid context metadata in the bisConfigurations
|
// Find any valid context metadata in the bisConfigurations
|
||||||
// assuming the bis configuration in the same bis subgroup
|
// assuming the bis configuration in the same bis subgroup
|
||||||
// will have the same context metadata
|
// will have the same context metadata
|
||||||
std::optional<AudioContext> config_context = std::nullopt;
|
std::optional<AudioContext> config_context = std::nullopt;
|
||||||
|
|
||||||
for (auto& p : configuration.bisConfigurations)
|
auto codec_requirement =
|
||||||
if (p.bisConfiguration.metadata.has_value()) {
|
getCodecRequirementBasedOnContext(requirement_context.bitmask, quality);
|
||||||
bool is_context_found = false;
|
std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv>
|
||||||
for (auto& metadata : p.bisConfiguration.metadata.value()) {
|
req_tag_map;
|
||||||
if (!metadata.has_value()) continue;
|
for (auto x : codec_requirement) req_tag_map[x.getTag()] = x;
|
||||||
if (metadata.value().getTag() ==
|
|
||||||
MetadataLtv::Tag::preferredAudioContexts) {
|
|
||||||
config_context = metadata.value()
|
|
||||||
.get<MetadataLtv::Tag::preferredAudioContexts>()
|
|
||||||
.values;
|
|
||||||
is_context_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (is_context_found) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not found context metadata in any of the bis configuration, assume matched
|
for (auto& bis_cfg : configuration.bisConfigurations) {
|
||||||
if (!config_context.has_value()) return true;
|
// Check every sub_bis_cfg to see which match
|
||||||
return (setting_context.bitmask & config_context.value().bitmask);
|
for (auto& x : bis_cfg.bisConfiguration.codecConfiguration) {
|
||||||
|
auto p = req_tag_map.find(x.getTag());
|
||||||
|
if (p == req_tag_map.end()) continue;
|
||||||
|
if (p->second != x) {
|
||||||
|
LOG(WARNING) << __func__ << ": does not match for context "
|
||||||
|
<< requirement_context.toString()
|
||||||
|
<< ", cfg = " << x.toString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ndk::ScopedAStatus
|
ndk::ScopedAStatus
|
||||||
|
@ -900,7 +1280,8 @@ LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration(
|
||||||
// We will allow empty capability input, match all settings with requirements.
|
// We will allow empty capability input, match all settings with requirements.
|
||||||
getBroadcastSettings();
|
getBroadcastSettings();
|
||||||
std::vector<LeAudioBroadcastConfigurationSetting> filtered_settings;
|
std::vector<LeAudioBroadcastConfigurationSetting> filtered_settings;
|
||||||
if (!in_remoteSinkAudioCapabilities.has_value()) {
|
if (!in_remoteSinkAudioCapabilities.has_value() ||
|
||||||
|
in_remoteSinkAudioCapabilities.value().empty()) {
|
||||||
LOG(INFO) << __func__ << ": Empty capability, get all broadcast settings";
|
LOG(INFO) << __func__ << ": Empty capability, get all broadcast settings";
|
||||||
filtered_settings = broadcast_settings;
|
filtered_settings = broadcast_settings;
|
||||||
} else {
|
} else {
|
||||||
|
@ -931,45 +1312,72 @@ LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration(
|
||||||
// Gather these suitable subgroup config in an array.
|
// Gather these suitable subgroup config in an array.
|
||||||
// If the setting can satisfy all requirement, we can return the setting
|
// If the setting can satisfy all requirement, we can return the setting
|
||||||
// with the filtered array.
|
// with the filtered array.
|
||||||
for (auto& setting : filtered_settings) {
|
|
||||||
LeAudioBroadcastConfigurationSetting matched_setting(setting);
|
|
||||||
matched_setting.subgroupsConfigurations.clear();
|
|
||||||
auto total_num_bis = 0;
|
|
||||||
|
|
||||||
bool matched_all_requirement = true;
|
auto context_bitmask =
|
||||||
|
in_requirement.subgroupConfigurationRequirements[0].audioContext.bitmask;
|
||||||
|
auto quality = in_requirement.subgroupConfigurationRequirements[0].quality;
|
||||||
|
LeAudioBroadcastConfigurationSetting return_setting =
|
||||||
|
getDefaultBroadcastSetting(context_bitmask, quality);
|
||||||
|
// Default setting
|
||||||
|
return_setting.numBis = 0;
|
||||||
|
return_setting.subgroupsConfigurations = {};
|
||||||
|
|
||||||
for (auto& sub_req : in_requirement.subgroupConfigurationRequirements) {
|
LeAudioDataPathConfiguration path;
|
||||||
bool is_matched = false;
|
path.isoDataPathConfiguration.isTransparent = true;
|
||||||
for (auto& sub_cfg : setting.subgroupsConfigurations) {
|
path.dataPathId = kIsoDataPathPlatformDefault;
|
||||||
// Match the context
|
|
||||||
if (!isSubgroupConfigurationMatchedContext(sub_req.audioContext,
|
// Each subreq, find a setting that match
|
||||||
sub_cfg))
|
for (auto& sub_req : in_requirement.subgroupConfigurationRequirements) {
|
||||||
continue;
|
bool is_setting_matched = false;
|
||||||
// Matching number of BIS
|
for (auto setting : filtered_settings) {
|
||||||
if (sub_req.bisNumPerSubgroup != sub_cfg.bisConfigurations.size())
|
bool is_matched = true;
|
||||||
continue;
|
// Check if every sub BIS config satisfy
|
||||||
// Currently will ignore quality matching.
|
for (auto& sub_group_config : setting.subgroupsConfigurations) {
|
||||||
matched_setting.subgroupsConfigurations.push_back(sub_cfg);
|
if (!isSubgroupConfigurationMatchedContext(
|
||||||
total_num_bis += sub_cfg.bisConfigurations.size();
|
sub_req.audioContext, sub_req.quality, sub_group_config)) {
|
||||||
is_matched = true;
|
is_matched = false;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
path.isoDataPathConfiguration.codecId =
|
||||||
|
sub_group_config.bisConfigurations[0].bisConfiguration.codecId;
|
||||||
|
// Also modify the subgroup config to match the context
|
||||||
|
modifySubgroupConfiguration(sub_group_config, context_bitmask);
|
||||||
}
|
}
|
||||||
// There is an unmatched requirement, this setting cannot be used
|
|
||||||
if (!is_matched) {
|
if (is_matched) {
|
||||||
matched_all_requirement = false;
|
is_setting_matched = true;
|
||||||
|
for (auto& sub_group_config : setting.subgroupsConfigurations)
|
||||||
|
return_setting.subgroupsConfigurations.push_back(sub_group_config);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matched_all_requirement) continue;
|
if (!is_setting_matched) {
|
||||||
|
LOG(WARNING) << __func__
|
||||||
matched_setting.numBis = total_num_bis;
|
<< ": Cannot find a setting that match requirement "
|
||||||
// Return the filtered setting if all requirement satified
|
<< sub_req.toString();
|
||||||
*_aidl_return = matched_setting;
|
return ndk::ScopedAStatus::ok();
|
||||||
return ndk::ScopedAStatus::ok();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(WARNING) << __func__ << ": Cannot match any requirement";
|
// Populate all numBis
|
||||||
|
for (auto& sub_group_config : return_setting.subgroupsConfigurations) {
|
||||||
|
for (auto& sub_bis_config : sub_group_config.bisConfigurations) {
|
||||||
|
return_setting.numBis += sub_bis_config.numBis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return_setting.phy = std::vector<Phy>(return_setting.numBis, Phy::TWO_M);
|
||||||
|
// Populate data path config
|
||||||
|
return_setting.dataPathConfiguration = path;
|
||||||
|
// TODO: Workaround for STEREO configs maxSduOctets being doubled
|
||||||
|
if (context_bitmask & (AudioContext::LIVE_AUDIO | AudioContext::GAME |
|
||||||
|
AudioContext::SOUND_EFFECTS |
|
||||||
|
AudioContext::UNSPECIFIED | AudioContext::MEDIA)) {
|
||||||
|
return_setting.maxSduOctets /= 2;
|
||||||
|
}
|
||||||
|
LOG(INFO) << __func__
|
||||||
|
<< ": Combined setting that match: " << return_setting.toString();
|
||||||
|
*_aidl_return = return_setting;
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -122,8 +122,9 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
|
||||||
bool isCapabilitiesMatchedCodecConfiguration(
|
bool isCapabilitiesMatchedCodecConfiguration(
|
||||||
std::vector<CodecSpecificConfigurationLtv>& codec_cfg,
|
std::vector<CodecSpecificConfigurationLtv>& codec_cfg,
|
||||||
std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities);
|
std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities);
|
||||||
bool isMatchedAseConfiguration(LeAudioAseConfiguration setting_cfg,
|
bool filterMatchedAseConfiguration(
|
||||||
LeAudioAseConfiguration requirement_cfg);
|
LeAudioAseConfiguration& setting_cfg,
|
||||||
|
const LeAudioAseConfiguration& requirement_cfg);
|
||||||
bool isMatchedBISConfiguration(
|
bool isMatchedBISConfiguration(
|
||||||
LeAudioBisConfiguration bis_cfg,
|
LeAudioBisConfiguration bis_cfg,
|
||||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
|
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
|
||||||
|
@ -138,7 +139,8 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
|
||||||
direction_configurations,
|
direction_configurations,
|
||||||
const std::vector<std::optional<AseDirectionRequirement>>& requirements,
|
const std::vector<std::optional<AseDirectionRequirement>>& requirements,
|
||||||
std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
|
std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
|
||||||
valid_direction_configurations);
|
valid_direction_configurations,
|
||||||
|
bool is_exact);
|
||||||
std::optional<LeAudioAseConfigurationSetting>
|
std::optional<LeAudioAseConfigurationSetting>
|
||||||
getCapabilitiesMatchedAseConfigurationSettings(
|
getCapabilitiesMatchedAseConfigurationSettings(
|
||||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
||||||
|
@ -148,7 +150,8 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
|
||||||
getRequirementMatchedAseConfigurationSettings(
|
getRequirementMatchedAseConfigurationSettings(
|
||||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
||||||
const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
|
const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
|
||||||
requirement);
|
requirement,
|
||||||
|
bool is_exact);
|
||||||
bool isMatchedQosRequirement(LeAudioAseQosConfiguration setting_qos,
|
bool isMatchedQosRequirement(LeAudioAseQosConfiguration setting_qos,
|
||||||
AseQosDirectionRequirement requirement_qos);
|
AseQosDirectionRequirement requirement_qos);
|
||||||
std::optional<LeAudioBroadcastConfigurationSetting>
|
std::optional<LeAudioBroadcastConfigurationSetting>
|
||||||
|
@ -160,10 +163,20 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
|
||||||
uint8_t direction,
|
uint8_t direction,
|
||||||
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
|
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
|
||||||
qosRequirement,
|
qosRequirement,
|
||||||
std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings);
|
std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings,
|
||||||
|
bool is_exact);
|
||||||
bool isSubgroupConfigurationMatchedContext(
|
bool isSubgroupConfigurationMatchedContext(
|
||||||
AudioContext requirement_context,
|
AudioContext requirement_context,
|
||||||
|
IBluetoothAudioProvider::BroadcastQuality quality,
|
||||||
LeAudioBroadcastSubgroupConfiguration configuration);
|
LeAudioBroadcastSubgroupConfiguration configuration);
|
||||||
|
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
|
||||||
|
matchWithRequirement(
|
||||||
|
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>&
|
||||||
|
matched_ase_configuration_settings,
|
||||||
|
const std::vector<
|
||||||
|
IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
|
||||||
|
in_requirements,
|
||||||
|
bool is_exact);
|
||||||
};
|
};
|
||||||
|
|
||||||
class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
|
class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
|
||||||
|
|
|
@ -4177,6 +4177,7 @@ class BluetoothAudioProviderLeAudioBroadcastHardwareAidl
|
||||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies();
|
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies();
|
||||||
sampling_rate.bitmask =
|
sampling_rate.bitmask =
|
||||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ48000 |
|
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ48000 |
|
||||||
|
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ24000 |
|
||||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ16000;
|
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ16000;
|
||||||
auto frame_duration =
|
auto frame_duration =
|
||||||
CodecSpecificCapabilitiesLtv::SupportedFrameDurations();
|
CodecSpecificCapabilitiesLtv::SupportedFrameDurations();
|
||||||
|
|
|
@ -267,7 +267,7 @@ void AudioSetConfigurationProviderJson::
|
||||||
ase_configuration_settings_.clear();
|
ase_configuration_settings_.clear();
|
||||||
configurations_.clear();
|
configurations_.clear();
|
||||||
auto loaded = LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios,
|
auto loaded = LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios,
|
||||||
CodecLocation::HOST);
|
CodecLocation::ADSP);
|
||||||
if (!loaded)
|
if (!loaded)
|
||||||
LOG(ERROR) << ": Unable to load le audio set configuration files.";
|
LOG(ERROR) << ": Unable to load le audio set configuration files.";
|
||||||
} else
|
} else
|
||||||
|
@ -371,7 +371,6 @@ void AudioSetConfigurationProviderJson::populateConfigurationData(
|
||||||
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU();
|
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU();
|
||||||
frame_sdu_structure.value = codec_frames_blocks_per_sdu;
|
frame_sdu_structure.value = codec_frames_blocks_per_sdu;
|
||||||
ase.codecConfiguration.push_back(frame_sdu_structure);
|
ase.codecConfiguration.push_back(frame_sdu_structure);
|
||||||
// TODO: Channel count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioSetConfigurationProviderJson::populateAseConfiguration(
|
void AudioSetConfigurationProviderJson::populateAseConfiguration(
|
||||||
|
@ -415,8 +414,53 @@ void AudioSetConfigurationProviderJson::populateAseConfiguration(
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioSetConfigurationProviderJson::populateAseQosConfiguration(
|
void AudioSetConfigurationProviderJson::populateAseQosConfiguration(
|
||||||
LeAudioAseQosConfiguration& qos,
|
LeAudioAseQosConfiguration& qos, const le_audio::QosConfiguration* qos_cfg,
|
||||||
const le_audio::QosConfiguration* qos_cfg) {
|
LeAudioAseConfiguration& ase) {
|
||||||
|
std::optional<CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU>
|
||||||
|
frameBlock = std::nullopt;
|
||||||
|
std::optional<CodecSpecificConfigurationLtv::FrameDuration> frameDuration =
|
||||||
|
std::nullopt;
|
||||||
|
std::optional<CodecSpecificConfigurationLtv::AudioChannelAllocation>
|
||||||
|
allocation = std::nullopt;
|
||||||
|
std::optional<CodecSpecificConfigurationLtv::OctetsPerCodecFrame> octet =
|
||||||
|
std::nullopt;
|
||||||
|
|
||||||
|
for (auto& cfg_ltv : ase.codecConfiguration) {
|
||||||
|
auto tag = cfg_ltv.getTag();
|
||||||
|
if (tag == CodecSpecificConfigurationLtv::codecFrameBlocksPerSDU) {
|
||||||
|
frameBlock =
|
||||||
|
cfg_ltv.get<CodecSpecificConfigurationLtv::codecFrameBlocksPerSDU>();
|
||||||
|
} else if (tag == CodecSpecificConfigurationLtv::frameDuration) {
|
||||||
|
frameDuration =
|
||||||
|
cfg_ltv.get<CodecSpecificConfigurationLtv::frameDuration>();
|
||||||
|
} else if (tag == CodecSpecificConfigurationLtv::audioChannelAllocation) {
|
||||||
|
allocation =
|
||||||
|
cfg_ltv.get<CodecSpecificConfigurationLtv::audioChannelAllocation>();
|
||||||
|
} else if (tag == CodecSpecificConfigurationLtv::octetsPerCodecFrame) {
|
||||||
|
octet = cfg_ltv.get<CodecSpecificConfigurationLtv::octetsPerCodecFrame>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int frameBlockValue = 1;
|
||||||
|
if (frameBlock.has_value()) frameBlockValue = frameBlock.value().value;
|
||||||
|
|
||||||
|
// Populate maxSdu
|
||||||
|
if (allocation.has_value() && octet.has_value()) {
|
||||||
|
auto channel_count = std::bitset<32>(allocation.value().bitmask).count();
|
||||||
|
qos.maxSdu = channel_count * octet.value().value * frameBlockValue;
|
||||||
|
}
|
||||||
|
// Populate sduIntervalUs
|
||||||
|
if (frameDuration.has_value()) {
|
||||||
|
switch (frameDuration.value()) {
|
||||||
|
case CodecSpecificConfigurationLtv::FrameDuration::US7500:
|
||||||
|
qos.sduIntervalUs = 7500;
|
||||||
|
break;
|
||||||
|
case CodecSpecificConfigurationLtv::FrameDuration::US10000:
|
||||||
|
qos.sduIntervalUs = 10000;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
qos.sduIntervalUs *= frameBlockValue;
|
||||||
|
}
|
||||||
qos.maxTransportLatencyMs = qos_cfg->max_transport_latency();
|
qos.maxTransportLatencyMs = qos_cfg->max_transport_latency();
|
||||||
qos.retransmissionNum = qos_cfg->retransmission_number();
|
qos.retransmissionNum = qos_cfg->retransmission_number();
|
||||||
}
|
}
|
||||||
|
@ -436,7 +480,7 @@ AudioSetConfigurationProviderJson::SetConfigurationFromFlatSubconfig(
|
||||||
populateAseConfiguration(ase, flat_subconfig, qos_cfg);
|
populateAseConfiguration(ase, flat_subconfig, qos_cfg);
|
||||||
|
|
||||||
// Translate into LeAudioAseQosConfiguration
|
// Translate into LeAudioAseQosConfiguration
|
||||||
populateAseQosConfiguration(qos, qos_cfg);
|
populateAseQosConfiguration(qos, qos_cfg, ase);
|
||||||
|
|
||||||
// Translate location to data path id
|
// Translate location to data path id
|
||||||
switch (location) {
|
switch (location) {
|
||||||
|
@ -453,6 +497,8 @@ AudioSetConfigurationProviderJson::SetConfigurationFromFlatSubconfig(
|
||||||
path.dataPathId = kIsoDataPathPlatformDefault;
|
path.dataPathId = kIsoDataPathPlatformDefault;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Move codecId to iso data path
|
||||||
|
path.isoDataPathConfiguration.codecId = ase.codecId.value();
|
||||||
|
|
||||||
direction_conf.aseConfiguration = ase;
|
direction_conf.aseConfiguration = ase;
|
||||||
direction_conf.qosConfiguration = qos;
|
direction_conf.qosConfiguration = qos;
|
||||||
|
@ -678,7 +724,8 @@ bool AudioSetConfigurationProviderJson::LoadScenariosFromFiles(
|
||||||
media_context.bitmask =
|
media_context.bitmask =
|
||||||
(AudioContext::ALERTS | AudioContext::INSTRUCTIONAL |
|
(AudioContext::ALERTS | AudioContext::INSTRUCTIONAL |
|
||||||
AudioContext::NOTIFICATIONS | AudioContext::EMERGENCY_ALARM |
|
AudioContext::NOTIFICATIONS | AudioContext::EMERGENCY_ALARM |
|
||||||
AudioContext::UNSPECIFIED | AudioContext::MEDIA);
|
AudioContext::UNSPECIFIED | AudioContext::MEDIA |
|
||||||
|
AudioContext::SOUND_EFFECTS);
|
||||||
|
|
||||||
AudioContext conversational_context = AudioContext();
|
AudioContext conversational_context = AudioContext();
|
||||||
conversational_context.bitmask =
|
conversational_context.bitmask =
|
||||||
|
|
|
@ -79,7 +79,7 @@ class AudioSetConfigurationProviderJson {
|
||||||
|
|
||||||
static void populateAseQosConfiguration(
|
static void populateAseQosConfiguration(
|
||||||
LeAudioAseQosConfiguration& qos,
|
LeAudioAseQosConfiguration& qos,
|
||||||
const le_audio::QosConfiguration* qos_cfg);
|
const le_audio::QosConfiguration* qos_cfg, LeAudioAseConfiguration& ase);
|
||||||
|
|
||||||
static AseDirectionConfiguration SetConfigurationFromFlatSubconfig(
|
static AseDirectionConfiguration SetConfigurationFromFlatSubconfig(
|
||||||
const le_audio::AudioSetSubConfiguration* flat_subconfig,
|
const le_audio::AudioSetSubConfiguration* flat_subconfig,
|
||||||
|
|
Loading…
Reference in a new issue