From 57861bdd0cf85ca5430dc37c528da57af0d5ba69 Mon Sep 17 00:00:00 2001 From: Bao Do Date: Thu, 23 May 2024 17:02:54 +0800 Subject: [PATCH] Additional context matching logic for LEA multi-codec When we match with the remote capabilities, we don't filter by preferred audio context, since they are suggestion. Instead, we populate settings into 2 separated vector: - Matched settings with matched preferred context, - Matched settings, We then filter by the first vector, then by the second vector. Bug: 306225778 Test: atest VtsHalBluetoothAudioTargetTest Change-Id: I03f322e8de509127d218a9de6b41dd39b2ebdcba --- .../default/LeAudioOffloadAudioProvider.cpp | 396 +++++++++++++----- .../default/LeAudioOffloadAudioProvider.h | 12 +- .../vts/VtsHalBluetoothAudioTargetTest.cpp | 1 + ...LeAudioAseConfigurationSettingProvider.cpp | 59 ++- ...thLeAudioAseConfigurationSettingProvider.h | 2 +- 5 files changed, 352 insertions(+), 118 deletions(-) diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp index 5002a1aeda..1f54e6ce2f 100644 --- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp @@ -175,12 +175,13 @@ bool LeAudioOffloadAudioProvider::filterCapabilitiesMatchedContext( if (!metadata.has_value()) continue; if (metadata.value().getTag() == MetadataLtv::Tag::preferredAudioContexts) { // Check all pref audio context to see if anything matched - auto& context = metadata.value() - .get() - .values; - if (setting_context.bitmask & context.bitmask) { + auto& prefer_context = + metadata.value() + .get() + .values; + if (setting_context.bitmask & prefer_context.bitmask) { // New mask with matched capability - setting_context.bitmask &= context.bitmask; + setting_context.bitmask &= prefer_context.bitmask; return true; } } @@ -219,8 +220,9 @@ bool LeAudioOffloadAudioProvider::isMatchedAudioChannel( /*cfg_channel*/, CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts& /*capability_channel*/) { + // Simply ignore. + // Later can use additional capabilities to match requirement. bool isMatched = true; - // TODO: how to match? return isMatched; } @@ -317,22 +319,34 @@ bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedCodecConfiguration( return true; } -bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration( - LeAudioAseConfiguration setting_cfg, - LeAudioAseConfiguration requirement_cfg) { +bool isMonoConfig( + CodecSpecificConfigurationLtv::AudioChannelAllocation allocation) { + 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 // Also match if no CodecId requirement if (requirement_cfg.codecId.has_value()) { if (!setting_cfg.codecId.has_value()) return false; if (!isMatchedValidCodec(setting_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; } } - if (requirement_cfg.targetLatency == - LeAudioAseConfiguration::TargetLatency::UNDEFINED || + if (requirement_cfg.targetLatency != + LeAudioAseConfiguration::TargetLatency::UNDEFINED && 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; } // Ignore PHY requirement @@ -346,11 +360,24 @@ bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration( for (auto requirement_cfg : requirement_cfg.codecConfiguration) { // Directly compare CodecSpecificConfigurationLtv auto cfg = cfg_tag_map.find(requirement_cfg.getTag()); + // Config not found for this requirement, cannot match if (cfg == cfg_tag_map.end()) { + LOG(WARNING) << __func__ << ": Config not found for the requirement " + << requirement_cfg.toString(); 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) { + LOG(WARNING) << __func__ + << ": Config doesn't match the requirement, cfg = " + << cfg->second.toString() + << ", req = " << requirement_cfg.toString(); return false; } } @@ -395,35 +422,132 @@ void LeAudioOffloadAudioProvider::filterCapabilitiesAseDirectionConfiguration( } } +int getLeAudioAseConfigurationAllocationBitmask(LeAudioAseConfiguration cfg) { + for (auto cfg_ltv : cfg.codecConfiguration) { + if (cfg_ltv.getTag() == + CodecSpecificConfigurationLtv::Tag::audioChannelAllocation) { + return cfg_ltv + .get() + .bitmask; + } + } + return 0; +} + +int getCountFromBitmask(int bitmask) { + return std::bitset<32>(bitmask).count(); +} + +std::optional findValidMonoConfig( + std::vector& valid_direction_configurations, + int bitmask) { + for (auto& cfg : valid_direction_configurations) { + int cfg_bitmask = + getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration); + if (getCountFromBitmask(cfg_bitmask) <= 1) { + LOG(INFO) << __func__ << ": Found a mono config for the mono requirement"; + // 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() + .bitmask = bitmask; + return cfg; + } + } + } + } + return std::nullopt; +} + +std::vector getValidConfigurationsFromAllocation( + int req_allocation_bitmask, + std::vector& valid_direction_configurations) { + // Prefer the same allocation_bitmask + int channel_count = getCountFromBitmask(req_allocation_bitmask); + for (auto& cfg : valid_direction_configurations) { + int cfg_bitmask = + getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration); + if (cfg_bitmask == req_allocation_bitmask) { + LOG(INFO) << __func__ + << ": Found an exact match for the requirement allocation of " + << cfg_bitmask; + return {cfg}; + } + } + // No exact match found + 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 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( std::optional>>& direction_configurations, const std::vector>& requirements, std::optional>>& valid_direction_configurations) { + // For every requirement, find the matched ase configuration + if (!direction_configurations.has_value()) return; + if (!valid_direction_configurations.has_value()) { valid_direction_configurations = std::vector>(); } - // For every requirement, find the matched ase configuration - if (!direction_configurations.has_value()) return; + for (auto& requirement : requirements) { if (!requirement.has_value()) continue; + LOG(INFO) << __func__ << ": Testing for requirement = " + << requirement.value().toString(); + auto req_allocation_bitmask = getLeAudioAseConfigurationAllocationBitmask( + requirement.value().aseConfiguration); + auto req_channel_count = getCountFromBitmask(req_allocation_bitmask); + + auto temp = std::vector(); + for (auto direction_configuration : direction_configurations.value()) { if (!direction_configuration.has_value()) continue; - if (!isMatchedAseConfiguration( + if (!filterMatchedAseConfiguration( direction_configuration.value().aseConfiguration, requirement.value().aseConfiguration)) continue; // Valid if match any requirement. - valid_direction_configurations.value().push_back(direction_configuration); - break; + temp.push_back(direction_configuration.value()); + } + + // Get the best matching config based on channel allocation + auto total_cfg_channel_count = 0; + auto req_valid_configs = + getValidConfigurationsFromAllocation(req_allocation_bitmask, temp); + // 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) { + LOG(WARNING) << __func__ + << ": Wrong channel count, req = " << req_channel_count + << ", total = " << total_cfg_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 +568,6 @@ LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings( .flags = setting.flags, .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 // for the input direction @@ -495,15 +615,11 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings( requirement.audioContext.bitmask) return std::nullopt; + LOG(INFO) << __func__ + << ": Checking if the setting match: " << setting.toString(); // Further filter setting's context setting.audioContext.bitmask &= requirement.audioContext.bitmask; - // Check requirement for the correct direction - const std::optional>>* - direction_requirement; - std::vector>* - direction_configuration; - // Create a new LeAudioAseConfigurationSetting to return LeAudioAseConfigurationSetting filtered_setting{ .audioContext = setting.audioContext, @@ -515,7 +631,10 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings( filterRequirementAseDirectionConfiguration( setting.sinkAseConfiguration, requirement.sinkAseRequirement.value(), filtered_setting.sinkAseConfiguration); - if (!filtered_setting.sinkAseConfiguration.has_value()) return std::nullopt; + if (!filtered_setting.sinkAseConfiguration.has_value()) { + LOG(WARNING) << __func__ << "Setting's sink doesn't match!"; + return std::nullopt; + } } if (requirement.sourceAseRequirement.has_value()) { @@ -523,82 +642,21 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings( setting.sourceAseConfiguration, requirement.sourceAseRequirement.value(), filtered_setting.sourceAseConfiguration); - if (!filtered_setting.sourceAseConfiguration.has_value()) + if (!filtered_setting.sourceAseConfiguration.has_value()) { + LOG(WARNING) << __func__ << "Setting's source doesn't match!"; return std::nullopt; + } } return filtered_setting; } -// For each requirement, a valid ASE configuration will satify: -// - matched with any sink capability (if presented) -// - OR matched with any source capability (if presented) -// - and the setting need to pass the requirement -ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration( - const std::optional>>& - in_remoteSinkAudioCapabilities, - const std::optional>>& - in_remoteSourceAudioCapabilities, +std::vector +LeAudioOffloadAudioProvider::matchWithRequirement( + std::vector& + matched_ase_configuration_settings, const std::vector& - in_requirements, - std::vector* - _aidl_return) { - // Get all configuration settings - std::vector - ase_configuration_settings = - BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings(); - - if (!in_remoteSinkAudioCapabilities.has_value() && - !in_remoteSourceAudioCapabilities.has_value()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } - - // Each setting consist of source and sink AseDirectionConfiguration vector - // Filter every sink capability - std::vector - matched_ase_configuration_settings; - - if (in_remoteSinkAudioCapabilities.has_value()) { - // Matching each setting with any remote capabilities - for (auto& setting : ase_configuration_settings) { - for (auto& capability : in_remoteSinkAudioCapabilities.value()) { - if (!capability.has_value()) continue; - auto filtered_ase_configuration_setting = - getCapabilitiesMatchedAseConfigurationSettings( - setting, capability.value(), kLeAudioDirectionSink); - if (filtered_ase_configuration_setting.has_value()) { - matched_ase_configuration_settings.push_back( - filtered_ase_configuration_setting.value()); - } - } - } - } - - // Combine filter every source capability - if (in_remoteSourceAudioCapabilities.has_value()) { - // Matching each setting with any remote capabilities - for (auto& setting : ase_configuration_settings) { - for (auto& capability : in_remoteSourceAudioCapabilities.value()) { - if (!capability.has_value()) continue; - auto filtered_ase_configuration_setting = - getCapabilitiesMatchedAseConfigurationSettings( - setting, capability.value(), kLeAudioDirectionSource); - if (filtered_ase_configuration_setting.has_value()) { - // Put into the same list - // possibly duplicated, filtered by requirement later - matched_ase_configuration_settings.push_back( - filtered_ase_configuration_setting.value()); - } - } - } - } - - if (matched_ase_configuration_settings.empty()) { - LOG(WARNING) << __func__ << ": No setting matched the capability"; - return ndk::ScopedAStatus::ok(); - } + in_requirements) { // Each requirement will match with a valid setting std::vector result; for (auto& requirement : in_requirements) { @@ -626,7 +684,103 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration( break; } } + return result; +} +// For each requirement, a valid ASE configuration will satify: +// - matched with any sink capability (if presented) +// - OR matched with any source capability (if presented) +// - and the setting need to pass the requirement +ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration( + const std::optional>>& + in_remoteSinkAudioCapabilities, + const std::optional>>& + in_remoteSourceAudioCapabilities, + const std::vector& + in_requirements, + std::vector* + _aidl_return) { + // Get all configuration settings + std::vector + ase_configuration_settings = + BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings(); + + if (!in_remoteSinkAudioCapabilities.has_value() && + !in_remoteSourceAudioCapabilities.has_value()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + // Split out preferred and non-preferred settings based on context + // 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 + matched_ase_configuration_settings; + // Matched ASE configuration with non-preferred audio context + std::vector + non_prefer_matched_ase_configuration_settings; + + if (in_remoteSinkAudioCapabilities.has_value()) + // Matching each setting with any remote capabilities + for (auto& setting : ase_configuration_settings) + for (auto& capability : in_remoteSinkAudioCapabilities.value()) { + if (!capability.has_value()) continue; + auto filtered_ase_configuration_setting = + getCapabilitiesMatchedAseConfigurationSettings( + setting, capability.value(), kLeAudioDirectionSink); + if (filtered_ase_configuration_setting.has_value()) { + // Push to non-prefer first for the broadest matching possible + non_prefer_matched_ase_configuration_settings.push_back( + 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 + if (in_remoteSourceAudioCapabilities.has_value()) + // Matching each setting with any remote capabilities + for (auto& setting : ase_configuration_settings) + for (auto& capability : in_remoteSourceAudioCapabilities.value()) { + if (!capability.has_value()) continue; + auto filtered_ase_configuration_setting = + getCapabilitiesMatchedAseConfigurationSettings( + setting, capability.value(), kLeAudioDirectionSource); + if (filtered_ase_configuration_setting.has_value()) { + // Put into the same list + // possibly duplicated, filtered by requirement later + // Push to non-prefer first for the broadest matching possible + non_prefer_matched_ase_configuration_settings.push_back( + 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()); + } + } + } + + auto result = + matchWithRequirement(matched_ase_configuration_settings, in_requirements); + if (result.empty()) { + LOG(WARNING) << __func__ + << ": Cannot match with preferred context settings"; + result = matchWithRequirement(non_prefer_matched_ase_configuration_settings, + in_requirements); + if (result.empty()) + LOG(WARNING) << __func__ + << ": Cannot match with non preferred context settings"; + } *_aidl_return = result; return ndk::ScopedAStatus::ok(); }; @@ -642,7 +796,6 @@ bool LeAudioOffloadAudioProvider::isMatchedQosRequirement( requirement_qos.maxTransportLatencyMs) { return false; } - // Ignore other parameters, as they are not populated in the setting_qos return true; } @@ -680,17 +833,26 @@ LeAudioOffloadAudioProvider::getDirectionQosConfiguration( // Get a list of all matched AseDirectionConfiguration // for the input direction - std::vector>* - direction_configuration = nullptr; + std::optional>> + direction_configuration = std::nullopt; if (direction == kLeAudioDirectionSink) { if (!setting.sinkAseConfiguration.has_value()) continue; - direction_configuration = &setting.sinkAseConfiguration.value(); + direction_configuration.emplace(setting.sinkAseConfiguration.value()); } else { 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(); + + for (auto& cfg : direction_configuration.value()) { if (!cfg.has_value()) continue; // If no requirement, return the first QoS if (!direction_qos_requirement.has_value()) { @@ -701,14 +863,30 @@ LeAudioOffloadAudioProvider::getDirectionQosConfiguration( // Try to match the ASE configuration // and QoS with requirement if (!cfg.value().qosConfiguration.has_value()) continue; - if (isMatchedAseConfiguration( + if (filterMatchedAseConfiguration( cfg.value().aseConfiguration, direction_qos_requirement.value().aseConfiguration) && isMatchedQosRequirement(cfg.value().qosConfiguration.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); + 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; diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h index 06cd405fd7..afa2efda60 100644 --- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h +++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h @@ -122,8 +122,9 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider { bool isCapabilitiesMatchedCodecConfiguration( std::vector& codec_cfg, std::vector codec_capabilities); - bool isMatchedAseConfiguration(LeAudioAseConfiguration setting_cfg, - LeAudioAseConfiguration requirement_cfg); + bool filterMatchedAseConfiguration( + LeAudioAseConfiguration& setting_cfg, + const LeAudioAseConfiguration& requirement_cfg); bool isMatchedBISConfiguration( LeAudioBisConfiguration bis_cfg, const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities); @@ -164,6 +165,13 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider { bool isSubgroupConfigurationMatchedContext( AudioContext requirement_context, LeAudioBroadcastSubgroupConfiguration configuration); + std::vector + matchWithRequirement( + std::vector& + matched_ase_configuration_settings, + const std::vector< + IBluetoothAudioProvider::LeAudioConfigurationRequirement>& + in_requirements); }; class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider { diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp index ec63831301..66cca0b128 100644 --- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp +++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp @@ -4177,6 +4177,7 @@ class BluetoothAudioProviderLeAudioBroadcastHardwareAidl CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies(); sampling_rate.bitmask = CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ48000 | + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ24000 | CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ16000; auto frame_duration = CodecSpecificCapabilitiesLtv::SupportedFrameDurations(); diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp index 5429a8fca7..780154ad8a 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp +++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp @@ -267,7 +267,7 @@ void AudioSetConfigurationProviderJson:: ase_configuration_settings_.clear(); configurations_.clear(); auto loaded = LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios, - CodecLocation::HOST); + CodecLocation::ADSP); if (!loaded) LOG(ERROR) << ": Unable to load le audio set configuration files."; } else @@ -371,7 +371,6 @@ void AudioSetConfigurationProviderJson::populateConfigurationData( CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU(); frame_sdu_structure.value = codec_frames_blocks_per_sdu; ase.codecConfiguration.push_back(frame_sdu_structure); - // TODO: Channel count } void AudioSetConfigurationProviderJson::populateAseConfiguration( @@ -415,8 +414,53 @@ void AudioSetConfigurationProviderJson::populateAseConfiguration( } void AudioSetConfigurationProviderJson::populateAseQosConfiguration( - LeAudioAseQosConfiguration& qos, - const le_audio::QosConfiguration* qos_cfg) { + LeAudioAseQosConfiguration& qos, const le_audio::QosConfiguration* qos_cfg, + LeAudioAseConfiguration& ase) { + std::optional + frameBlock = std::nullopt; + std::optional frameDuration = + std::nullopt; + std::optional + allocation = std::nullopt; + std::optional octet = + std::nullopt; + + for (auto& cfg_ltv : ase.codecConfiguration) { + auto tag = cfg_ltv.getTag(); + if (tag == CodecSpecificConfigurationLtv::codecFrameBlocksPerSDU) { + frameBlock = + cfg_ltv.get(); + } else if (tag == CodecSpecificConfigurationLtv::frameDuration) { + frameDuration = + cfg_ltv.get(); + } else if (tag == CodecSpecificConfigurationLtv::audioChannelAllocation) { + allocation = + cfg_ltv.get(); + } else if (tag == CodecSpecificConfigurationLtv::octetsPerCodecFrame) { + octet = cfg_ltv.get(); + } + } + + 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.retransmissionNum = qos_cfg->retransmission_number(); } @@ -436,7 +480,7 @@ AudioSetConfigurationProviderJson::SetConfigurationFromFlatSubconfig( populateAseConfiguration(ase, flat_subconfig, qos_cfg); // Translate into LeAudioAseQosConfiguration - populateAseQosConfiguration(qos, qos_cfg); + populateAseQosConfiguration(qos, qos_cfg, ase); // Translate location to data path id switch (location) { @@ -453,6 +497,8 @@ AudioSetConfigurationProviderJson::SetConfigurationFromFlatSubconfig( path.dataPathId = kIsoDataPathPlatformDefault; break; } + // Move codecId to iso data path + path.isoDataPathConfiguration.codecId = ase.codecId.value(); direction_conf.aseConfiguration = ase; direction_conf.qosConfiguration = qos; @@ -678,7 +724,8 @@ bool AudioSetConfigurationProviderJson::LoadScenariosFromFiles( media_context.bitmask = (AudioContext::ALERTS | AudioContext::INSTRUCTIONAL | AudioContext::NOTIFICATIONS | AudioContext::EMERGENCY_ALARM | - AudioContext::UNSPECIFIED | AudioContext::MEDIA); + AudioContext::UNSPECIFIED | AudioContext::MEDIA | + AudioContext::SOUND_EFFECTS); AudioContext conversational_context = AudioContext(); conversational_context.bitmask = diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.h b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.h index ce91fca12b..6639009742 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.h +++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.h @@ -79,7 +79,7 @@ class AudioSetConfigurationProviderJson { static void populateAseQosConfiguration( LeAudioAseQosConfiguration& qos, - const le_audio::QosConfiguration* qos_cfg); + const le_audio::QosConfiguration* qos_cfg, LeAudioAseConfiguration& ase); static AseDirectionConfiguration SetConfigurationFromFlatSubconfig( const le_audio::AudioSetSubConfiguration* flat_subconfig,