Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d6c8a4b3 authored by Bao Do's avatar Bao Do Committed by Gerrit Code Review
Browse files

Merge changes Iefd406fc,I0f6cfbda into main

* changes:
  New parsing logic and configuration matching for LE Audio
  Update the default set configuration file and format.
parents ed095e62 64956920
Loading
Loading
Loading
Loading
+182 −112
Original line number Diff line number Diff line
@@ -121,6 +121,40 @@ bool LeAudioOffloadAudioProvider::isValid(const SessionType& sessionType) {
  return (sessionType == session_type_);
}

std::string getSettingOutputString(
    IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting) {
  std::stringstream ss;
  std::string name = "";
  if (!setting.sinkAseConfiguration.has_value() &&
      !setting.sourceAseConfiguration.has_value())
    return "";
  std::vector<
      std::optional<LeAudioAseConfigurationSetting::AseDirectionConfiguration>>*
      directionAseConfiguration;
  if (setting.sinkAseConfiguration.has_value() &&
      !setting.sinkAseConfiguration.value().empty())
    directionAseConfiguration = &setting.sinkAseConfiguration.value();
  else
    directionAseConfiguration = &setting.sourceAseConfiguration.value();
  for (auto& aseConfiguration : *directionAseConfiguration) {
    if (aseConfiguration.has_value() &&
        aseConfiguration.value().aseConfiguration.metadata.has_value()) {
      for (auto& meta :
           aseConfiguration.value().aseConfiguration.metadata.value()) {
        if (meta.has_value() &&
            meta.value().getTag() == MetadataLtv::vendorSpecific) {
          auto k = meta.value().get<MetadataLtv::vendorSpecific>().opaqueValue;
          name = std::string(k.begin(), k.end());
          break;
        }
      }
    }
  }

  ss << "setting name: " << name << ", setting: " << setting.toString();
  return ss.str();
}

ndk::ScopedAStatus LeAudioOffloadAudioProvider::startSession(
    const std::shared_ptr<IBluetoothAudioPort>& host_if,
    const AudioConfiguration& audio_config,
@@ -218,15 +252,24 @@ bool LeAudioOffloadAudioProvider::isMatchedFrameDuration(
  return false;
}

int getCountFromBitmask(int bitmask) {
  return std::bitset<32>(bitmask).count();
}

bool LeAudioOffloadAudioProvider::isMatchedAudioChannel(
    CodecSpecificConfigurationLtv::AudioChannelAllocation&
    /*cfg_channel*/,
    CodecSpecificConfigurationLtv::AudioChannelAllocation& cfg_channel,
    CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
    /*capability_channel*/) {
  // Simply ignore.
  // Later can use additional capabilities to match requirement.
  bool isMatched = true;
  return isMatched;
        capability_channel) {
  int count = getCountFromBitmask(cfg_channel.bitmask);
  if (count == 1 &&
      !(capability_channel.bitmask &
        CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts::ONE))
    return false;
  if (count == 2 &&
      !(capability_channel.bitmask &
        CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts::TWO))
    return false;
  return true;
}

bool LeAudioOffloadAudioProvider::isMatchedCodecFramesPerSDU(
@@ -322,12 +365,6 @@ bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedCodecConfiguration(
  return true;
}

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) {
@@ -337,9 +374,6 @@ bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
    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;
    }
  }
@@ -347,9 +381,6 @@ bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
  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
@@ -365,8 +396,6 @@ bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
    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;
    }

@@ -377,10 +406,6 @@ bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
      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;
    }
  }
@@ -437,10 +462,6 @@ int getLeAudioAseConfigurationAllocationBitmask(LeAudioAseConfiguration cfg) {
  return 0;
}

int getCountFromBitmask(int bitmask) {
  return std::bitset<32>(bitmask).count();
}

std::optional<AseDirectionConfiguration> findValidMonoConfig(
    std::vector<AseDirectionConfiguration>& valid_direction_configurations,
    int bitmask) {
@@ -505,14 +526,13 @@ std::vector<AseDirectionConfiguration> getValidConfigurationsFromAllocation(
  return {};
}

// Check and filter each index to see if it's a match.
void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
    std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
        direction_configurations,
    const std::vector<std::optional<AseDirectionRequirement>>& requirements,
    std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
        valid_direction_configurations,
    bool is_exact) {
  // For every requirement, find the matched ase configuration
        valid_direction_configurations) {
  if (!direction_configurations.has_value()) return;

  if (!valid_direction_configurations.has_value()) {
@@ -520,38 +540,55 @@ void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
        std::vector<std::optional<AseDirectionConfiguration>>();
  }

  for (auto& requirement : requirements) {
    if (!requirement.has_value()) continue;
  // Exact matching process
  // Need to respect the number of device
  for (int i = 0; i < requirements.size(); ++i) {
    auto requirement = requirements[i];
    auto direction_configuration = direction_configurations.value()[i];
    if (!direction_configuration.has_value()) {
      valid_direction_configurations = std::nullopt;
      return;
    }
    auto cfg = direction_configuration.value();
    if (!filterMatchedAseConfiguration(cfg.aseConfiguration,
                                       requirement.value().aseConfiguration)) {
      valid_direction_configurations = std::nullopt;
      return;  // No way to match
    }
    // For exact match, we require this direction to have the same allocation.
    // If stereo, need stereo.
    // If mono, need mono (modified to the correct required allocation)
    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()) {
      if (!direction_configuration.has_value()) continue;
      if (!filterMatchedAseConfiguration(
              direction_configuration.value().aseConfiguration,
              requirement.value().aseConfiguration))
        continue;
      // Valid if match any requirement.
      temp.push_back(direction_configuration.value());
    int req_channel_count = getCountFromBitmask(req_allocation_bitmask);
    int cfg_bitmask =
        getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration);
    int cfg_channel_count = getCountFromBitmask(cfg_bitmask);
    if (req_channel_count <= 1) {
      // MONO case, is a match if also mono, modify to the same allocation
      if (cfg_channel_count > 1) {
        valid_direction_configurations = std::nullopt;
        return;  // Not a match
      }
      // 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 = req_allocation_bitmask;
          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) {
    } else {
      // STEREO case, is a match if same allocation
      if (req_allocation_bitmask != cfg_bitmask) {
        valid_direction_configurations = std::nullopt;
      return;
        return;  // Not a match
      }
    }
    // Push to list if valid
    valid_direction_configurations.value().push_back(cfg);
  }
}

@@ -612,8 +649,8 @@ LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings(
std::optional<LeAudioAseConfigurationSetting>
LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
    IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
    const IBluetoothAudioProvider::LeAudioConfigurationRequirement& requirement,
    bool is_exact) {
    const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
        requirement) {
  // Try to match context in metadata.
  if ((setting.audioContext.bitmask & requirement.audioContext.bitmask) !=
      requirement.audioContext.bitmask)
@@ -629,10 +666,29 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
      .flags = setting.flags,
  };

  // The number of AseDirectionRequirement in the requirement
  // is the number of device.

  // The exact matching process is as follow:
  // 1. Setting direction has the same number of cfg (ignore when null require)
  // 2. For each index, it's a 1-1 filter / mapping.

  if (requirement.sinkAseRequirement.has_value() &&
      requirement.sinkAseRequirement.value().size() !=
          setting.sinkAseConfiguration.value().size()) {
    return std::nullopt;
  }

  if (requirement.sourceAseRequirement.has_value() &&
      requirement.sourceAseRequirement.value().size() !=
          setting.sourceAseConfiguration.value().size()) {
    return std::nullopt;
  }

  if (requirement.sinkAseRequirement.has_value()) {
    filterRequirementAseDirectionConfiguration(
        setting.sinkAseConfiguration, requirement.sinkAseRequirement.value(),
        filtered_setting.sinkAseConfiguration, is_exact);
        filtered_setting.sinkAseConfiguration);
    if (!filtered_setting.sinkAseConfiguration.has_value()) {
      return std::nullopt;
    }
@@ -642,7 +698,7 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
    filterRequirementAseDirectionConfiguration(
        setting.sourceAseConfiguration,
        requirement.sourceAseRequirement.value(),
        filtered_setting.sourceAseConfiguration, is_exact);
        filtered_setting.sourceAseConfiguration);
    if (!filtered_setting.sourceAseConfiguration.has_value()) {
      return std::nullopt;
    }
@@ -656,8 +712,7 @@ LeAudioOffloadAudioProvider::matchWithRequirement(
    std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>&
        matched_ase_configuration_settings,
    const std::vector<IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
        in_requirements,
    bool is_exact) {
        in_requirements) {
  // Each requirement will match with a valid setting
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
  for (auto& requirement : in_requirements) {
@@ -667,12 +722,12 @@ LeAudioOffloadAudioProvider::matchWithRequirement(

    for (auto& setting : matched_ase_configuration_settings) {
      auto filtered_ase_configuration_setting =
          getRequirementMatchedAseConfigurationSettings(setting, requirement,
                                                        is_exact);
          getRequirementMatchedAseConfigurationSettings(setting, requirement);
      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();
        LOG(INFO) << __func__ << ": Result found: "
                  << getSettingOutputString(
                         filtered_ase_configuration_setting.value());
        // Found a matched setting, ignore other settings
        is_matched = true;
        break;
@@ -690,8 +745,8 @@ LeAudioOffloadAudioProvider::matchWithRequirement(
}

// For each requirement, a valid ASE configuration will satify:
// - matched with any sink capability (if presented)
// - OR matched with any source capability (if presented)
// - matched with the sink capability (if presented)
// - AND matched with the source capability (if presented)
// - and the setting need to pass the requirement
ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
    const std::optional<std::vector<
@@ -719,49 +774,55 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
  // -> 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>
      sink_matched_ase_configuration_settings;
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      matched_ase_configuration_settings;
  // Matched ASE configuration with non-preferred audio context
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      sink_non_prefer_matched_ase_configuration_settings;
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      non_prefer_matched_ase_configuration_settings;

  if (in_remoteSinkAudioCapabilities.has_value())
    // Matching each setting with any remote capabilities
  // A setting must match both source and sink.
  // First filter all setting matched with sink capability
  if (in_remoteSinkAudioCapabilities.has_value()) {
    for (auto& setting : ase_configuration_settings)
      for (auto& capability : in_remoteSinkAudioCapabilities.value()) {
        if (!capability.has_value()) continue;
        // LOG(DEBUG) << __func__ << ": " << capability.value().toString();
        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(
          sink_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(
            sink_matched_ase_configuration_settings.push_back(
                filtered_ase_configuration_setting.value());
          }
        }
      }
  } else {
    sink_matched_ase_configuration_settings = ase_configuration_settings;
    sink_non_prefer_matched_ase_configuration_settings =
        ase_configuration_settings;
  }

  // Combine filter every source capability
  if (in_remoteSourceAudioCapabilities.has_value())
    // Matching each setting with any remote capabilities
    for (auto& setting : ase_configuration_settings)
  if (in_remoteSourceAudioCapabilities.has_value()) {
    // Prefer context
    for (auto& setting : sink_matched_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,
@@ -772,43 +833,52 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
        }
      }

    // Non prefer context
    for (auto& setting : sink_non_prefer_matched_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()) {
          // Push to non-prefer first for the broadest matching possible
          non_prefer_matched_ase_configuration_settings.push_back(
              filtered_ase_configuration_setting.value());
        }
      }
  } else {
    matched_ase_configuration_settings =
        sink_matched_ase_configuration_settings;
    non_prefer_matched_ase_configuration_settings =
        sink_non_prefer_matched_ase_configuration_settings;
  }

  // Matching priority list:
  // Preferred context - exact match with allocation
  // Any context - exact match with allocation
  // Preferred context - loose match with allocation
  // Any context - loose match with allocation

  // A loose match will attempt to return 2 settings with the
  // combined allocation bitmask equal the required allocation.
  // For example, we can return 2 link (left link and right link) when
  // the requirement required 1 (left + right) link.
  auto result = matchWithRequirement(matched_ase_configuration_settings,
                                     in_requirements, true);
  if (result.empty()) {
    LOG(WARNING)
        << __func__
        << ": Cannot match with preferred context settings - exact match";
    result = matchWithRequirement(non_prefer_matched_ase_configuration_settings,
                                  in_requirements, true);

  LOG(DEBUG) << __func__ << ": Called with requirement: ";
  for (auto& requirement : in_requirements) {
    LOG(DEBUG) << __func__ << " requirement: " << requirement.toString();
  }
  if (result.empty()) {
    LOG(WARNING)
        << __func__
        << ": Cannot match with non-preferred context settings - exact match";
    result = matchWithRequirement(matched_ase_configuration_settings,
                                  in_requirements, false);

  LOG(DEBUG) << __func__ << ": List of settings with the same context:";
  for (auto& setting : matched_ase_configuration_settings) {
    LOG(DEBUG) << __func__ << ": " << getSettingOutputString(setting);
  }

  auto result =
      matchWithRequirement(matched_ase_configuration_settings, in_requirements);
  if (result.empty()) {
    LOG(WARNING) << __func__
                 << ": Cannot match with preferred context settings - "
                    "non-exact match";
                 << ": Cannot match with preferred context settings";
    result = matchWithRequirement(non_prefer_matched_ase_configuration_settings,
                                  in_requirements, false);
                                  in_requirements);
  }
  if (result.empty())
  if (result.empty()) {
    LOG(ERROR) << __func__
               << ": Cannot match with non preferred context settings - "
                  "non-exact match";
               << ": Cannot match with non-preferred context settings";
  }
  *_aidl_return = result;
  return ndk::ScopedAStatus::ok();
};
+3 −6
Original line number Diff line number Diff line
@@ -139,8 +139,7 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
          direction_configurations,
      const std::vector<std::optional<AseDirectionRequirement>>& requirements,
      std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
          valid_direction_configurations,
      bool is_exact);
          valid_direction_configurations);
  std::optional<LeAudioAseConfigurationSetting>
  getCapabilitiesMatchedAseConfigurationSettings(
      IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
@@ -150,8 +149,7 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
  getRequirementMatchedAseConfigurationSettings(
      IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
      const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
          requirement,
      bool is_exact);
          requirement);
  bool isMatchedQosRequirement(LeAudioAseQosConfiguration setting_qos,
                               AseQosDirectionRequirement requirement_qos);
  std::optional<LeAudioBroadcastConfigurationSetting>
@@ -175,8 +173,7 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
          matched_ase_configuration_settings,
      const std::vector<
          IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
          in_requirements,
      bool is_exact);
          in_requirements);
};

class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
+6 −6
Original line number Diff line number Diff line
@@ -81,9 +81,9 @@ cc_library_shared {
    ],
    required: [
        "aidl_audio_set_configurations_bfbs",
        "aidl_audio_set_configurations_json",
        "aidl_default_audio_set_configurations_json",
        "aidl_audio_set_scenarios_bfbs",
        "aidl_audio_set_scenarios_json",
        "aidl_default_audio_set_scenarios_json",
        "hfp_codec_capabilities_xml",
    ],
}
@@ -215,9 +215,9 @@ prebuilt_etc {
}

prebuilt_etc {
    name: "aidl_audio_set_scenarios_json",
    name: "aidl_default_audio_set_scenarios_json",
    src: "le_audio_configuration_set/audio_set_scenarios.json",
    filename: "aidl_audio_set_scenarios.json",
    filename: "aidl_default_audio_set_scenarios.json",
    sub_dir: "aidl/le_audio",
    vendor: true,
}
@@ -239,9 +239,9 @@ prebuilt_etc {
}

prebuilt_etc {
    name: "aidl_audio_set_configurations_json",
    name: "aidl_default_audio_set_configurations_json",
    src: "le_audio_configuration_set/audio_set_configurations.json",
    filename: "aidl_audio_set_configurations.json",
    filename: "aidl_default_audio_set_configurations.json",
    sub_dir: "aidl/le_audio",
    vendor: true,
}
+73 −29

File changed.

Preview size limit exceeded, changes collapsed.

+5 −2
Original line number Diff line number Diff line
@@ -73,19 +73,22 @@ class AudioSetConfigurationProviderJson {
          flat_codec_specific_params);

  static void populateAseConfiguration(
      LeAudioAseConfiguration& ase,
      const std::string& name, LeAudioAseConfiguration& ase,
      const le_audio::AudioSetSubConfiguration* flat_subconfig,
      const le_audio::QosConfiguration* qos_cfg);

  static void populateAseQosConfiguration(
      LeAudioAseQosConfiguration& qos,
      const le_audio::QosConfiguration* qos_cfg, LeAudioAseConfiguration& ase);
      const le_audio::QosConfiguration* qos_cfg, LeAudioAseConfiguration& ase,
      uint8_t ase_channel_cnt);

  static AseDirectionConfiguration SetConfigurationFromFlatSubconfig(
      const std::string& name,
      const le_audio::AudioSetSubConfiguration* flat_subconfig,
      const le_audio::QosConfiguration* qos_cfg, CodecLocation location);

  static void processSubconfig(
      const std::string& name,
      const le_audio::AudioSetSubConfiguration* subconfig,
      const le_audio::QosConfiguration* qos_cfg,
      std::vector<std::optional<AseDirectionConfiguration>>&
Loading