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:
Bao Do 2024-06-06 06:01:00 +00:00 committed by Automerger Merge Worker
commit aa1b56973e
5 changed files with 630 additions and 161 deletions

View file

@ -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();
}; };

View file

@ -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 {

View file

@ -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();

View file

@ -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 =

View file

@ -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,