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

Commit e79e4dea authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

leaudio: Improve offloader behaviour

With this patch offloader is aware about number of CISes for the group
even whole group is not connected. Also it might be aware on which CISes are
connected based on the allocation value.

If flag
persist.bluetooth.leaudio.offloader.downmix_fallback is
set to false (default setup), the offloader will get complete list with allocation
already set to final allocation on stream start. There will be no updates of this
list during the streaming, but in this case offloader has no knowledge
about connection state  of CIS.

If persist.bluetooth.leaudio.offloader.downmix_fallback is
set to true, offloader will be notified about connection/disconnection
CIS using allocation value

Examples:
1) Group is banded headphones using one CIS to transmit two channels
   (left and right)

   In this case, offloader will get only one CIS with allocation set to
   stereo

2) Group is a earbuds which uses two CISes for single channel each, but
   only one earbud was connected.

   In this case offloader will get list of two CISes. The one which is
   connected will have allocation Stereo (left | right) and the one
   which is not connected will have allocation equal 0. Offloader is
   expected to mix riht and left

Bug: 231084798
Sponsor: @siyuanh
Test: atest BluetoothInstrumentationTests
Merged-In: Ic205348e45c4b8ecb3889f586d867675f0ce8931
Change-Id: Ic205348e45c4b8ecb3889f586d867675f0ce8931
(cherry picked from commit 9275204c)
parent fa14757d
Loading
Loading
Loading
Loading
+42 −20
Original line number Diff line number Diff line
@@ -2551,13 +2551,6 @@ class LeAudioClientImpl : public LeAudioClient {
          lc3_setup_encoder(dt_us, sr_hz, af_hz, lc3_encoder_left_mem);
      lc3_encoder_right =
          lc3_setup_encoder(dt_us, sr_hz, af_hz, lc3_encoder_right_mem);

    } else if (CodecManager::GetInstance()->GetCodecLocation() ==
               le_audio::types::CodecLocation::ADSP) {
      CodecManager::GetInstance()->UpdateActiveSourceAudioConfig(
          *stream_conf, remote_delay_ms,
          std::bind(&LeAudioUnicastClientAudioSource::UpdateAudioConfigToHal,
                    leAudioClientAudioSource, std::placeholders::_1));
    }

    leAudioClientAudioSource->UpdateRemoteDelay(remote_delay_ms);
@@ -2617,14 +2610,7 @@ class LeAudioClientImpl : public LeAudioClient {
          lc3_setup_decoder(dt_us, sr_hz, af_hz, lc3_decoder_left_mem);
      lc3_decoder_right =
          lc3_setup_decoder(dt_us, sr_hz, af_hz, lc3_decoder_right_mem);
    } else if (CodecManager::GetInstance()->GetCodecLocation() ==
               le_audio::types::CodecLocation::ADSP) {
      CodecManager::GetInstance()->UpdateActiveSinkAudioConfig(
          *stream_conf, remote_delay_ms,
          std::bind(&LeAudioUnicastClientAudioSink::UpdateAudioConfigToHal,
                    leAudioClientAudioSink, std::placeholders::_1));
    }

    leAudioClientAudioSink->UpdateRemoteDelay(remote_delay_ms);
    leAudioClientAudioSink->ConfirmStreamingRequest();
    audio_receiver_state_ = AudioState::STARTED;
@@ -3515,16 +3501,52 @@ class LeAudioClientImpl : public LeAudioClient {
    }
  }

  void updateOffloaderIfNeeded(LeAudioDeviceGroup* group) {
    if (CodecManager::GetInstance()->GetCodecLocation() !=
        le_audio::types::CodecLocation::ADSP) {
      return;
    }

    LOG_INFO("Group %p, group_id %d", group, group->group_id_);

    const auto* stream_conf = &group->stream_conf;

    if (stream_conf->sink_offloader_changed) {
      LOG_INFO("Update sink offloader streams");
      uint16_t remote_delay_ms =
          group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink);
      CodecManager::GetInstance()->UpdateActiveSourceAudioConfig(
          *stream_conf, remote_delay_ms,
          std::bind(&LeAudioUnicastClientAudioSource::UpdateAudioConfigToHal,
                    leAudioClientAudioSource, std::placeholders::_1));
      group->StreamOffloaderUpdated(le_audio::types::kLeAudioDirectionSink);
    }

    if (stream_conf->source_offloader_changed) {
      LOG_INFO("Update source offloader streams");
      uint16_t remote_delay_ms =
          group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource);
      CodecManager::GetInstance()->UpdateActiveSinkAudioConfig(
          *stream_conf, remote_delay_ms,
          std::bind(&LeAudioUnicastClientAudioSink::UpdateAudioConfigToHal,
                    leAudioClientAudioSink, std::placeholders::_1));
      group->StreamOffloaderUpdated(le_audio::types::kLeAudioDirectionSource);
    }
  }

  void StatusReportCb(int group_id, GroupStreamStatus status) {
    LOG(INFO) << __func__ << "status: " << static_cast<int>(status)
              << " audio_sender_state_: " << audio_sender_state_
              << " audio_receiver_state_: " << audio_receiver_state_;
    LOG_INFO("status: %d , audio_sender_state %s, audio_receiver_state %s",
             static_cast<int>(status),
             bluetooth::common::ToString(audio_sender_state_).c_str(),
             bluetooth::common::ToString(audio_receiver_state_).c_str());
    LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
    switch (status) {
      case GroupStreamStatus::STREAMING:
        LOG_ASSERT(group_id == active_group_id_)
            << __func__ << " invalid group id " << group_id
            << " active_group_id_ " << active_group_id_;
        ASSERT_LOG(group_id == active_group_id_, "invalid group id %d!=%d",
                   group_id, active_group_id_);

        updateOffloaderIfNeeded(group);

        if (audio_sender_state_ == AudioState::READY_TO_START)
          StartSendingAudio(group_id);
        if (audio_receiver_state_ == AudioState::READY_TO_START)
+2 −2
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ struct codec_manager_impl {
          update_receiver) {
    if (stream_conf.sink_streams.empty()) return;

    sink_config.stream_map = std::move(stream_conf.sink_streams);
    sink_config.stream_map = std::move(stream_conf.sink_offloader_streams);
    // TODO: set the default value 16 for now, would change it if we support
    // mode bits_per_sample
    sink_config.bits_per_sample = 16;
@@ -107,7 +107,7 @@ struct codec_manager_impl {
          update_receiver) {
    if (stream_conf.source_streams.empty()) return;

    source_config.stream_map = std::move(stream_conf.source_streams);
    source_config.stream_map = std::move(stream_conf.source_offloader_streams);
    // TODO: set the default value 16 for now, would change it if we support
    // mode bits_per_sample
    source_config.bits_per_sample = 16;
+104 −0
Original line number Diff line number Diff line
@@ -109,6 +109,7 @@ int LeAudioDeviceGroup::NumOfConnected(types::LeAudioContextType context_type) {
void LeAudioDeviceGroup::ClearSinksFromConfiguration(void) {
  LOG_INFO("Group %p, group_id %d", this, group_id_);
  stream_conf.sink_streams.clear();
  stream_conf.sink_offloader_streams.clear();
  stream_conf.sink_audio_channel_allocation = 0;
  stream_conf.sink_num_of_channels = 0;
  stream_conf.sink_num_of_devices = 0;
@@ -121,6 +122,7 @@ void LeAudioDeviceGroup::ClearSinksFromConfiguration(void) {
void LeAudioDeviceGroup::ClearSourcesFromConfiguration(void) {
  LOG_INFO("Group %p, group_id %d", this, group_id_);
  stream_conf.source_streams.clear();
  stream_conf.source_offloader_streams.clear();
  stream_conf.source_audio_channel_allocation = 0;
  stream_conf.source_num_of_channels = 0;
  stream_conf.source_num_of_devices = 0;
@@ -1618,6 +1620,108 @@ bool LeAudioDeviceGroup::IsMetadataChanged(
  return false;
}

void LeAudioDeviceGroup::StreamOffloaderUpdated(uint8_t direction) {
  if (direction == le_audio::types::kLeAudioDirectionSource) {
    stream_conf.source_offloader_changed = false;
  } else {
    stream_conf.sink_offloader_changed = false;
  }
}

void LeAudioDeviceGroup::CreateStreamVectorForOffloader(uint8_t direction) {
  if (CodecManager::GetInstance()->GetCodecLocation() !=
      le_audio::types::CodecLocation::ADSP) {
    return;
  }

  CisType cis_type;
  std::vector<std::pair<uint16_t, uint32_t>>* streams;
  std::vector<std::pair<uint16_t, uint32_t>>* offloader_streams;
  std::string tag;
  uint32_t available_allocations = 0;
  bool* changed_flag;
  if (direction == le_audio::types::kLeAudioDirectionSource) {
    changed_flag = &stream_conf.source_offloader_changed;
    cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE;
    streams = &stream_conf.source_streams;
    offloader_streams = &stream_conf.source_offloader_streams;
    tag = "Source";
    available_allocations = AdjustAllocationForOffloader(
        stream_conf.source_audio_channel_allocation);
  } else {
    changed_flag = &stream_conf.sink_offloader_changed;
    cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SINK;
    streams = &stream_conf.sink_streams;
    offloader_streams = &stream_conf.sink_offloader_streams;
    tag = "Sink";
    available_allocations =
        AdjustAllocationForOffloader(stream_conf.sink_audio_channel_allocation);
  }

  if (available_allocations == 0) {
    LOG_ERROR("There is no CIS connected");
    return;
  }

  if (offloader_streams->size() > 0) {
    /* We are here because of the CIS modification during streaming.
     * this makes sense only when downmixing is enabled so we can notify
     * offloader about connected / disconnected CISes. If downmixing is disabled
     * then there is not need to notify offloader as it has all the informations
     * already */
    if (!downmix_fallback_) {
      LOG_INFO("Downmixing disabled - nothing to do");
      return;
    }
  }

  offloader_streams->clear();
  *changed_flag = true;

  bool not_all_cises_connected = false;
  if (available_allocations != codec_spec_conf::kLeAudioLocationStereo) {
    not_all_cises_connected = true;
  }

  /* Note: For the offloader case we simplify allocation to only Left and Right.
   * If we need 2 CISes and only one is connected, the connected one will have
   * allocation set to stereo (left | right) and other one will have allocation
   * set to 0. Offloader in this case shall mix left and right and send it on
   * connected CIS. If there is only single CIS with stereo allocation, it means
   * that peer device support channel count 2 and offloader shall send two
   * channels in the single CIS.
   */

  for (auto& cis_entry : cises_) {
    if ((cis_entry.type == CisType::CIS_TYPE_BIDIRECTIONAL ||
         cis_entry.type == cis_type) &&
        cis_entry.conn_handle != 0) {
      uint32_t allocation = 0;
      for (const auto& s : *streams) {
        if (s.first == cis_entry.conn_handle) {
          allocation = AdjustAllocationForOffloader(s.second);
          if (not_all_cises_connected && downmix_fallback_) {
            /* Tell offloader to mix on this CIS.*/
            allocation = codec_spec_conf::kLeAudioLocationStereo;
          }
          break;
        }
      }

      if (allocation == 0 && !downmix_fallback_) {
        /* Take missing allocation for that one .*/
        allocation =
            codec_spec_conf::kLeAudioLocationStereo & ~available_allocations;
      }

      LOG_INFO("%s: Cis handle 0x%04x, allocation  0x%08x", tag.c_str(),
               cis_entry.conn_handle, allocation);
      offloader_streams->emplace_back(
          std::make_pair(cis_entry.conn_handle, allocation));
    }
  }
}

types::LeAudioContextType LeAudioDeviceGroup::GetCurrentContextType(void) {
  return active_context_type_;
}
+9 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include "gatt_api.h"
#include "le_audio_types.h"
#include "osi/include/alarm.h"
#include "osi/include/properties.h"
#include "raw_address.h"

namespace le_audio {
@@ -210,7 +211,9 @@ class LeAudioDeviceGroup {
        pending_update_available_contexts_(std::nullopt),
        target_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE),
        current_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE),
        context_type_(types::LeAudioContextType::UNINITIALIZED) {}
        context_type_(types::LeAudioContextType::UNINITIALIZED) {
    downmix_fallback_ = osi_property_get_bool(kDownmixFallback, false);
  }
  ~LeAudioDeviceGroup(void);

  void AddNode(const std::shared_ptr<LeAudioDevice>& leAudioDevice);
@@ -286,6 +289,8 @@ class LeAudioDeviceGroup {
  bool IsContextSupported(types::LeAudioContextType group_context_type);
  bool IsMetadataChanged(types::LeAudioContextType group_context_type,
                         int ccid);
  void CreateStreamVectorForOffloader(uint8_t direction);
  void StreamOffloaderUpdated(uint8_t direction);

  inline types::AseState GetState(void) const { return current_state_; }
  void SetState(types::AseState state) {
@@ -336,6 +341,9 @@ class LeAudioDeviceGroup {
           const set_configurations::AudioSetConfiguration*>
      active_context_to_configuration_map;

  static constexpr char kDownmixFallback[] =
      "persist.bluetooth.leaudio.offloader.downmix_fallback";
  bool downmix_fallback_;
  types::AseState target_state_;
  types::AseState current_state_;
  types::LeAudioContextType context_type_;
+15 −0
Original line number Diff line number Diff line
@@ -525,6 +525,21 @@ uint8_t GetMaxCodecFramesPerSduFromPac(const acs_ac_record* pac) {
  return 1;
}

uint32_t AdjustAllocationForOffloader(uint32_t allocation) {
  if ((allocation & codec_spec_conf::kLeAudioLocationAnyLeft) &&
      (allocation & codec_spec_conf::kLeAudioLocationAnyRight)) {
    return codec_spec_conf::kLeAudioLocationStereo;
  }
  if (allocation & codec_spec_conf::kLeAudioLocationAnyLeft) {
    return codec_spec_conf::kLeAudioLocationFrontLeft;
  }

  if (allocation & codec_spec_conf::kLeAudioLocationAnyRight) {
    return codec_spec_conf::kLeAudioLocationFrontRight;
  }
  return 0;
}

namespace types {
std::ostream& operator<<(std::ostream& os, const types::CigState& state) {
  static const char* char_value_[4] = {"NONE", "CREATING", "CREATED",
Loading