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

Commit 9d342132 authored by Henri Chataing's avatar Henri Chataing
Browse files

Implement initiator role for codec extensibility

Let the provider choose the codec configuration based on remote
supported codecs.

Bug: 305780384
Bug: 308686081
Test: m com.android.btservices
Change-Id: Ifb94fae45c023b9d353a4b7544dd5a144eb145ce
parent 83e8a5bb
Loading
Loading
Loading
Loading
+22 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

#pragma once

#include <iomanip>
#include <sstream>
#include <vector>

#include "a2dp_error_codes.h"
@@ -103,11 +105,12 @@ struct a2dp_configuration {
    std::ostringstream os;
    os << "A2dpConfiguration{";
    os << "remote_seid: " << remote_seid;
    os << "codec_index: " << codec_parameters.codec_type;
    os << ", codec_index: " << codec_parameters.codec_type;
    os << ", codec_config: {";
    for (int i = 0; i < AVDT_CODEC_SIZE; i++) {
      os << std::hex << std::setw(2) << std::setfill('0')
      os << "0x" << std::hex << std::setw(2) << std::setfill('0')
         << static_cast<int>(codec_config[i]);
      if (i != AVDT_CODEC_SIZE - 1) os << ",";
    }
    os << "}";
    os << "}";
@@ -118,6 +121,23 @@ struct a2dp_configuration {
struct a2dp_remote_capabilities {
  int seid;
  uint8_t const* capabilities;

  inline std::string toString() const {
    std::ostringstream os;
    os << "A2dpRemoteCapabilities{";
    os << "seid: " << seid;
    os << ", capabilities: {";
    if (capabilities != nullptr) {
      for (int i = 0; i < AVDT_CODEC_SIZE; i++) {
        os << "0x" << std::hex << std::setw(2) << std::setfill('0')
           << static_cast<int>(capabilities[i]);
        if (i != AVDT_CODEC_SIZE - 1) os << ",";
      }
    }
    os << "}";
    os << "}";
    return os.str();
  }
};

// Query the codec selection fromt the audio HAL.
+176 −9
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <base/logging.h>

#include <mutex>
#include <optional>
#include <vector>

#include "audio_hal_interface/a2dp_encoding.h"
@@ -40,6 +41,7 @@
#include "osi/include/osi.h"  // UNUSED_ATTR
#include "stack/include/a2dp_codec_api.h"
#include "stack/include/a2dp_error_codes.h"
#include "stack/include/a2dp_ext.h"
#include "stack/include/avdt_api.h"
#include "stack/include/bt_hdr.h"
#include "stack/include/bt_types.h"
@@ -664,6 +666,23 @@ class BtaAvCo {
  const BtaAvCoSep* AttemptSinkCodecSelection(
      const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer);

  /**
   * Let the HAL offload provider select codec configuration.
   *
   * @param p_peer the peer to use
   * @param configuration configuration from the offload provider
   */
  std::optional<::bluetooth::audio::a2dp::provider::a2dp_configuration>
  GetProviderCodecConfiguration(BtaAvCoPeer* p_peer);

  /**
   * Select the HAL proposed configuration.
   */
  BtaAvCoSep* SelectProviderCodecConfiguration(
      BtaAvCoPeer* p_peer,
      const ::bluetooth::audio::a2dp::provider::a2dp_configuration&
          provider_codec_config);

  /**
   * Check if a peer SEP has content protection enabled.
   *
@@ -911,6 +930,10 @@ void BtaAvCo::ProcessDiscoveryResult(tBTA_AV_HNDL bta_av_handle,
}

static void bta_av_co_store_peer_codectype(const BtaAvCoPeer* p_peer);
static bool bta_av_co_should_select_hardware_codec(
    const A2dpCodecConfig& software_config,
    const ::bluetooth::audio::a2dp::provider::a2dp_configuration&
        hardware_config);

tA2DP_STATUS BtaAvCo::ProcessSourceGetConfig(
    tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address,
@@ -1811,25 +1834,117 @@ bool BtaAvCo::AudioSepHasContentProtection(const BtaAvCoSep* p_sep) {
  return true;
}

const BtaAvCoSep* BtaAvCo::SelectSourceCodec(BtaAvCoPeer* p_peer) {
  const BtaAvCoSep* p_sink = nullptr;
std::optional<::bluetooth::audio::a2dp::provider::a2dp_configuration>
BtaAvCo::GetProviderCodecConfiguration(BtaAvCoPeer* p_peer) {
  // Gather peer codec capabilities.
  std::vector<::bluetooth::audio::a2dp::provider::a2dp_remote_capabilities>
      a2dp_remote_caps;
  for (size_t index = 0; index < p_peer->num_sup_sinks; index++) {
    const BtaAvCoSep* p_sink = &p_peer->sinks[index];
    auto& capabilities = a2dp_remote_caps.emplace_back();
    capabilities.seid = p_sink->seid;
    capabilities.capabilities = p_sink->codec_caps;
  }

  // Get the configuration of the preferred codec as codec hint.
  btav_a2dp_codec_config_t codec_config =
      p_peer->GetCodecs()->orderedSourceCodecs().front()->getCodecUserConfig();

  // Pass all gathered codec capabilities to the provider
  return ::bluetooth::audio::a2dp::provider::get_a2dp_configuration(
      p_peer->addr, a2dp_remote_caps, codec_config);
}

BtaAvCoSep* BtaAvCo::SelectProviderCodecConfiguration(
    BtaAvCoPeer* p_peer,
    const ::bluetooth::audio::a2dp::provider::a2dp_configuration&
        provider_codec_config) {
  // Configure the selected offload codec for the active peer.
  // This function _must_ have the same external behaviour as
  // AttemptSourceCodecSelection, except the configuration
  // is provided by the HAL rather than derived locally.

  LOG_INFO("Configuration=%s", provider_codec_config.toString().c_str());

  // Identify the selected sink.
  auto* p_sink =
      FindPeerSink(p_peer, provider_codec_config.codec_parameters.codec_type);
  ASSERT_LOG(p_sink != nullptr, "Unable to find the selected codec config");

  // Identify the selected codec.
  auto* codec_config = reinterpret_cast<A2dpCodecConfigExt*>(
      p_peer->GetCodecs()->findSourceCodecConfig(
          provider_codec_config.codec_parameters.codec_type));
  ASSERT_LOG(codec_config != nullptr,
             "Unable to find the selected codec config");

  // Update the vendor codec parameters and codec configuration.
  codec_config->setCodecConfig(
      provider_codec_config.codec_parameters,
      provider_codec_config.codec_config,
      provider_codec_config.vendor_specific_parameters);

  // Select the codec config.
  p_peer->GetCodecs()->setCurrentCodecConfig(codec_config);
  p_peer->p_sink = p_sink;
  SaveNewCodecConfig(p_peer, provider_codec_config.codec_config,
                     p_sink->num_protect, p_sink->protect_info);

  return p_sink;
}

const BtaAvCoSep* BtaAvCo::SelectSourceCodec(BtaAvCoPeer* p_peer) {
  // Update all selectable codecs.
  // This is needed to update the selectable parameters for each codec.
  // NOTE: The selectable codec info is used only for informational purpose.
  UpdateAllSelectableSourceCodecs(p_peer);

  // Select the codec
  // Query the preferred codec configuration for offloaded codecs.
  auto provider_codec_config = GetProviderCodecConfiguration(p_peer);

  // Query the preferred codec configuration for software codecs.
  A2dpCodecConfig* software_codec_config = nullptr;
  for (const auto& iter : p_peer->GetCodecs()->orderedSourceCodecs()) {
    VLOG(1) << __func__ << ": trying codec " << iter->name();
    p_sink = AttemptSourceCodecSelection(*iter, p_peer);
    if (p_sink != nullptr) {
      VLOG(1) << __func__ << ": selected codec " << iter->name();
    if (::bluetooth::audio::a2dp::provider::supports_codec(
            iter->codecIndex())) {
      continue;
    }

    // Find the peer Sink for the codec
    uint8_t new_codec_config[AVDT_CODEC_SIZE];
    const BtaAvCoSep* p_sink = FindPeerSink(p_peer, iter->codecIndex());

    if (p_sink == nullptr) {
      LOG_VERBOSE("peer Sink for codec %s not found", iter->name().c_str());
      continue;
    }

    if (!p_peer->GetCodecs()->setCodecConfig(
            p_sink->codec_caps, true /* is_capability */, new_codec_config,
            false /* select_current_codec */)) {
      LOG_VERBOSE("cannot set source codec %s", iter->name().c_str());
    } else {
      LOG_VERBOSE("feasible to set source codec %s", iter->name().c_str());
      software_codec_config = iter;
      break;
    }
    VLOG(1) << __func__ << ": cannot use codec " << iter->name();
  }
  return p_sink;

  if (provider_codec_config.has_value() &&
      (software_codec_config == nullptr ||
       bta_av_co_should_select_hardware_codec(*software_codec_config,
                                              provider_codec_config.value()))) {
    // Select hardware offload codec configuration
    return SelectProviderCodecConfiguration(p_peer,
                                            provider_codec_config.value());
  }

  if (software_codec_config != nullptr) {
    // Select software codec configuration
    return AttemptSourceCodecSelection(*software_codec_config, p_peer);
  }

  return nullptr;
}

const BtaAvCoSep* BtaAvCo::SelectSinkCodec(BtaAvCoPeer* p_peer) {
@@ -2161,6 +2276,58 @@ static void bta_av_co_store_peer_codectype(const BtaAvCoPeer* p_peer) {
                                 peer_codec_type, IOT_CONF_BYTE_NUM_1);
}

static bool bta_av_co_should_select_hardware_codec(
    const A2dpCodecConfig& software_config,
    const ::bluetooth::audio::a2dp::provider::a2dp_configuration&
        hardware_config) {
  btav_a2dp_codec_index_t software_codec_index = software_config.codecIndex();
  btav_a2dp_codec_index_t hardware_offload_index =
      hardware_config.codec_parameters.codec_type;

  // Prioritize any offload codec except SBC and AAC
  if (A2DP_GetCodecType(hardware_config.codec_config) ==
      A2DP_MEDIA_CT_NON_A2DP) {
    LOG_VERBOSE("select hardware codec: %s",
                A2DP_CodecIndexStr(hardware_offload_index));
    return true;
  }
  // Prioritize LDAC, AptX HD and AptX over AAC and SBC offload codecs
  if (software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC ||
      software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD ||
      software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX) {
    LOG_VERBOSE("select software codec: %s",
                A2DP_CodecIndexStr(software_codec_index));
    return false;
  }
  // Prioritize AAC offload
  if (hardware_offload_index == BTAV_A2DP_CODEC_INDEX_SOURCE_AAC) {
    LOG_VERBOSE("select hardware codec: %s",
                A2DP_CodecIndexStr(hardware_offload_index));
    return true;
  }
  // Prioritize AAC software
  if (software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_AAC) {
    LOG_VERBOSE("select software codec: %s",
                A2DP_CodecIndexStr(software_codec_index));
    return false;
  }
  // Prioritize SBC offload
  if (hardware_offload_index == BTAV_A2DP_CODEC_INDEX_SOURCE_SBC) {
    LOG_VERBOSE("select hardware codec: %s",
                A2DP_CodecIndexStr(hardware_offload_index));
    return true;
  }
  // Prioritize SBC software
  if (software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_SBC) {
    LOG_VERBOSE("select software codec: %s",
                A2DP_CodecIndexStr(software_codec_index));
    return false;
  }
  LOG_ERROR("select unknown software codec: %s",
            A2DP_CodecIndexStr(software_codec_index));
  return false;
}

tA2DP_STATUS bta_av_co_audio_getconfig(tBTA_AV_HNDL bta_av_handle,
                                       const RawAddress& peer_address,
                                       uint8_t* p_codec_info,
+13 −3
Original line number Diff line number Diff line
@@ -667,15 +667,16 @@ bool A2dpCodecs::init() {
    LOG_ERROR("%s: no Source codecs were initialized", __func__);
  } else {
    for (auto iter : ordered_source_codecs_) {
      LOG_INFO("%s: initialized Source codec %s", __func__,
               iter->name().c_str());
      LOG_INFO("%s: initialized Source codec %s, idx %d", __func__,
               iter->name().c_str(), iter->codecIndex());
    }
  }
  if (ordered_sink_codecs_.empty()) {
    LOG_ERROR("%s: no Sink codecs were initialized", __func__);
  } else {
    for (auto iter : ordered_sink_codecs_) {
      LOG_INFO("%s: initialized Sink codec %s", __func__, iter->name().c_str());
      LOG_INFO("%s: initialized Sink codec %s, idx %d", __func__,
               iter->name().c_str(), iter->codecIndex());
    }
  }

@@ -693,6 +694,15 @@ A2dpCodecConfig* A2dpCodecs::findSourceCodecConfig(
  return iter->second;
}

A2dpCodecConfig* A2dpCodecs::findSourceCodecConfig(
    btav_a2dp_codec_index_t codec_index) {
  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);

  auto iter = indexed_codecs_.find(codec_index);
  if (iter == indexed_codecs_.end()) return nullptr;
  return iter->second;
}

A2dpCodecConfig* A2dpCodecs::findSinkCodecConfig(const uint8_t* p_codec_info) {
  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
  btav_a2dp_codec_index_t codec_index = A2DP_SinkCodecIndex(p_codec_info);
+4 −0
Original line number Diff line number Diff line
@@ -277,6 +277,10 @@ class A2dpCodecs {
  // Returns the Source codec if found, otherwise nullptr.
  A2dpCodecConfig* findSourceCodecConfig(const uint8_t* p_codec_info);

  // Finds the Source codec that corresponds to the A2DP codec index.
  // Returns the Source codec if found, otherwise nullptr.
  A2dpCodecConfig* findSourceCodecConfig(btav_a2dp_codec_index_t codec_index);

  // Finds the Sink codec that corresponds to the A2DP over-the-air
  // |p_codec_info| information.
  // Returns the Sink codec if found, otherwise nullptr.
+5 −0
Original line number Diff line number Diff line
@@ -44,6 +44,11 @@ A2dpCodecConfig* A2dpCodecs::findSourceCodecConfig(
  inc_func_call_count(__func__);
  return nullptr;
}
A2dpCodecConfig* A2dpCodecs::findSourceCodecConfig(
    btav_a2dp_codec_index_t /* codec_index */) {
  inc_func_call_count(__func__);
  return nullptr;
}
A2dpCodecConfig::A2dpCodecConfig(btav_a2dp_codec_index_t codec_index,
                                 const std::string& name,
                                 btav_a2dp_codec_priority_t codec_priority)