Loading system/bta/le_audio/client.cc +58 −121 Original line number Diff line number Diff line Loading @@ -2241,109 +2241,19 @@ class LeAudioClientImpl : public LeAudioClient { chan_encoded.size()); } struct le_audio::stream_configuration* GetStreamConfigurationByDirection( LeAudioDeviceGroup* group, uint8_t direction) { struct le_audio::stream_configuration* stream_conf = &group->stream_conf; uint32_t sample_freq_hz = 0; uint32_t frame_duration_us = 0; uint32_t audio_channel_allocation = 0; uint16_t octets_per_frame = 0; uint16_t codec_frames_blocks_per_sdu = 0; LOG(INFO) << __func__ << " group_id: " << group->group_id_; /* This contains pair of cis handle and audio location */ std::vector<std::pair<uint16_t, uint32_t>> streams; for (auto* device = group->GetFirstActiveDevice(); device != nullptr; device = group->GetNextActiveDevice(device)) { auto* ase = device->GetFirstActiveAseByDirection(direction); for (; ase != nullptr; ase = device->GetNextActiveAseWithSameDirection(ase)) { streams.emplace_back(std::make_pair( ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation)); audio_channel_allocation |= *ase->codec_config.audio_channel_allocation; if (sample_freq_hz == 0) { sample_freq_hz = ase->codec_config.GetSamplingFrequencyHz(); } else { LOG_ASSERT(sample_freq_hz == ase->codec_config.GetSamplingFrequencyHz()) << __func__ << " sample freq mismatch: " << +sample_freq_hz << " != " << ase->codec_config.GetSamplingFrequencyHz(); } if (frame_duration_us == 0) { frame_duration_us = ase->codec_config.GetFrameDurationUs(); } else { LOG_ASSERT(frame_duration_us == ase->codec_config.GetFrameDurationUs()) << __func__ << " frame duration mismatch: " << +frame_duration_us << " != " << ase->codec_config.GetFrameDurationUs(); } if (octets_per_frame == 0) { octets_per_frame = *ase->codec_config.octets_per_codec_frame; } else { LOG_ASSERT(octets_per_frame == ase->codec_config.octets_per_codec_frame) << __func__ << " octets per frame mismatch: " << +octets_per_frame << " != " << *ase->codec_config.octets_per_codec_frame; const struct le_audio::stream_configuration* GetStreamSinkConfiguration( LeAudioDeviceGroup* group) { const struct le_audio::stream_configuration* stream_conf = &group->stream_conf; LOG_INFO("group_id: %d", group->group_id_); if (stream_conf->sink_streams.size() == 0) { return nullptr; } if (codec_frames_blocks_per_sdu == 0) { codec_frames_blocks_per_sdu = *ase->codec_config.codec_frames_blocks_per_sdu; } else { LOG_ASSERT(codec_frames_blocks_per_sdu == ase->codec_config.codec_frames_blocks_per_sdu) << __func__ << " codec_frames_blocks_per_sdu: " << +codec_frames_blocks_per_sdu << " != " << *ase->codec_config.codec_frames_blocks_per_sdu; } LOG(INFO) << __func__ << " Added CIS: " << +ase->cis_conn_hdl << " to stream. Allocation: " << +(*ase->codec_config.audio_channel_allocation) << " sample_freq: " << +sample_freq_hz << " frame_duration: " << +frame_duration_us << " octects per frame: " << +octets_per_frame << " codec_frame_blocks_per_sdu: " << +codec_frames_blocks_per_sdu; } } if (streams.empty()) return nullptr; if (direction == le_audio::types::kLeAudioDirectionSource) { stream_conf->source_streams = std::move(streams); stream_conf->source_sample_frequency_hz = sample_freq_hz; stream_conf->source_frame_duration_us = frame_duration_us; stream_conf->source_audio_channel_allocation = audio_channel_allocation; stream_conf->source_octets_per_codec_frame = octets_per_frame; stream_conf->source_codec_frames_blocks_per_sdu = codec_frames_blocks_per_sdu; } else if (direction == le_audio::types::kLeAudioDirectionSink) { stream_conf->sink_streams = std::move(streams); stream_conf->sink_sample_frequency_hz = sample_freq_hz; stream_conf->sink_frame_duration_us = frame_duration_us; stream_conf->sink_audio_channel_allocation = audio_channel_allocation; stream_conf->sink_octets_per_codec_frame = octets_per_frame; stream_conf->sink_codec_frames_blocks_per_sdu = codec_frames_blocks_per_sdu; } LOG(INFO) << __func__ << " configuration: " << stream_conf->conf->name; LOG_INFO("configuration: %s", stream_conf->conf->name.c_str()); return stream_conf; } struct le_audio::stream_configuration* GetStreamSinkConfiguration( LeAudioDeviceGroup* group) { return GetStreamConfigurationByDirection( group, le_audio::types::kLeAudioDirectionSink); } void OnAudioDataReady(const std::vector<uint8_t>& data) { if ((active_group_id_ == bluetooth::groups::kGroupUnknown) || (audio_sender_state_ != AudioState::STARTED)) Loading Loading @@ -2641,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); Loading @@ -2657,10 +2560,15 @@ class LeAudioClientImpl : public LeAudioClient { return true; } struct le_audio::stream_configuration* GetStreamSourceConfiguration( const struct le_audio::stream_configuration* GetStreamSourceConfiguration( LeAudioDeviceGroup* group) { return GetStreamConfigurationByDirection( group, le_audio::types::kLeAudioDirectionSource); const struct le_audio::stream_configuration* stream_conf = &group->stream_conf; if (stream_conf->source_streams.size() == 0) { return nullptr; } LOG_INFO("configuration: %s", stream_conf->conf->name.c_str()); return stream_conf; } void StartReceivingAudio(int group_id) { Loading Loading @@ -2702,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; Loading Loading @@ -3600,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) Loading system/bta/le_audio/codec_manager.cc +2 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading system/bta/le_audio/devices.cc +130 −0 Original line number Diff line number Diff line Loading @@ -106,9 +106,37 @@ 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; stream_conf.sink_sample_frequency_hz = 0; stream_conf.sink_codec_frames_blocks_per_sdu = 0; stream_conf.sink_octets_per_codec_frame = 0; stream_conf.sink_frame_duration_us = 0; } 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; stream_conf.source_sample_frequency_hz = 0; stream_conf.source_codec_frames_blocks_per_sdu = 0; stream_conf.source_octets_per_codec_frame = 0; stream_conf.source_frame_duration_us = 0; } void LeAudioDeviceGroup::CigClearCis(void) { LOG_INFO("group_id: %d", group_id_); cises_.clear(); ClearSinksFromConfiguration(); ClearSourcesFromConfiguration(); } void LeAudioDeviceGroup::Cleanup(void) { Loading Loading @@ -1592,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_; } Loading system/bta/le_audio/devices.h +11 −1 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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); Loading @@ -223,6 +226,8 @@ class LeAudioDeviceGroup { bool Activate(types::LeAudioContextType context_type); void Deactivate(void); void CigClearCis(void); void ClearSinksFromConfiguration(void); void ClearSourcesFromConfiguration(void); void Cleanup(void); LeAudioDevice* GetFirstDevice(void); LeAudioDevice* GetFirstDeviceWithActiveContext( Loading Loading @@ -284,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) { Loading Loading @@ -334,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_; Loading system/bta/le_audio/le_audio_client_test.cc +73 −0 Original line number Diff line number Diff line Loading @@ -772,6 +772,42 @@ class UnicastTestNoInit : public Test { stream_conf->source_num_of_devices++; stream_conf->source_num_of_channels += ase.codec_config.channel_count; stream_conf->source_audio_channel_allocation |= *ase.codec_config.audio_channel_allocation; if (stream_conf->source_sample_frequency_hz == 0) { stream_conf->source_sample_frequency_hz = ase.codec_config.GetSamplingFrequencyHz(); } else { ASSERT_LOG(stream_conf->source_sample_frequency_hz == ase.codec_config.GetSamplingFrequencyHz(), "sample freq mismatch: %d!=%d", stream_conf->source_sample_frequency_hz, ase.codec_config.GetSamplingFrequencyHz()); } if (stream_conf->source_octets_per_codec_frame == 0) { stream_conf->source_octets_per_codec_frame = *ase.codec_config.octets_per_codec_frame; } else { ASSERT_LOG(stream_conf->source_octets_per_codec_frame == *ase.codec_config.octets_per_codec_frame, "octets per frame mismatch: %d!=%d", stream_conf->source_octets_per_codec_frame, *ase.codec_config.octets_per_codec_frame); } if (stream_conf->source_codec_frames_blocks_per_sdu == 0) { stream_conf->source_codec_frames_blocks_per_sdu = *ase.codec_config.codec_frames_blocks_per_sdu; } else { ASSERT_LOG( stream_conf->source_codec_frames_blocks_per_sdu == *ase.codec_config.codec_frames_blocks_per_sdu, "codec_frames_blocks_per_sdu: %d!=%d", stream_conf->source_codec_frames_blocks_per_sdu, *ase.codec_config.codec_frames_blocks_per_sdu); } LOG_INFO( " Added Source Stream Configuration. CIS Connection " Loading Loading @@ -800,6 +836,43 @@ class UnicastTestNoInit : public Test { stream_conf->sink_num_of_channels += ase.codec_config.channel_count; stream_conf->sink_audio_channel_allocation |= *ase.codec_config.audio_channel_allocation; if (stream_conf->sink_sample_frequency_hz == 0) { stream_conf->sink_sample_frequency_hz = ase.codec_config.GetSamplingFrequencyHz(); } else { ASSERT_LOG(stream_conf->sink_sample_frequency_hz == ase.codec_config.GetSamplingFrequencyHz(), "sample freq mismatch: %d!=%d", stream_conf->sink_sample_frequency_hz, ase.codec_config.GetSamplingFrequencyHz()); } if (stream_conf->sink_octets_per_codec_frame == 0) { stream_conf->sink_octets_per_codec_frame = *ase.codec_config.octets_per_codec_frame; } else { ASSERT_LOG(stream_conf->sink_octets_per_codec_frame == *ase.codec_config.octets_per_codec_frame, "octets per frame mismatch: %d!=%d", stream_conf->sink_octets_per_codec_frame, *ase.codec_config.octets_per_codec_frame); } if (stream_conf->sink_codec_frames_blocks_per_sdu == 0) { stream_conf->sink_codec_frames_blocks_per_sdu = *ase.codec_config.codec_frames_blocks_per_sdu; } else { ASSERT_LOG( stream_conf->sink_codec_frames_blocks_per_sdu == *ase.codec_config.codec_frames_blocks_per_sdu, "codec_frames_blocks_per_sdu: %d!=%d", stream_conf->sink_codec_frames_blocks_per_sdu, *ase.codec_config.codec_frames_blocks_per_sdu); } LOG_INFO( " Added Sink Stream Configuration. CIS Connection " "Handle: %d" Loading Loading
system/bta/le_audio/client.cc +58 −121 Original line number Diff line number Diff line Loading @@ -2241,109 +2241,19 @@ class LeAudioClientImpl : public LeAudioClient { chan_encoded.size()); } struct le_audio::stream_configuration* GetStreamConfigurationByDirection( LeAudioDeviceGroup* group, uint8_t direction) { struct le_audio::stream_configuration* stream_conf = &group->stream_conf; uint32_t sample_freq_hz = 0; uint32_t frame_duration_us = 0; uint32_t audio_channel_allocation = 0; uint16_t octets_per_frame = 0; uint16_t codec_frames_blocks_per_sdu = 0; LOG(INFO) << __func__ << " group_id: " << group->group_id_; /* This contains pair of cis handle and audio location */ std::vector<std::pair<uint16_t, uint32_t>> streams; for (auto* device = group->GetFirstActiveDevice(); device != nullptr; device = group->GetNextActiveDevice(device)) { auto* ase = device->GetFirstActiveAseByDirection(direction); for (; ase != nullptr; ase = device->GetNextActiveAseWithSameDirection(ase)) { streams.emplace_back(std::make_pair( ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation)); audio_channel_allocation |= *ase->codec_config.audio_channel_allocation; if (sample_freq_hz == 0) { sample_freq_hz = ase->codec_config.GetSamplingFrequencyHz(); } else { LOG_ASSERT(sample_freq_hz == ase->codec_config.GetSamplingFrequencyHz()) << __func__ << " sample freq mismatch: " << +sample_freq_hz << " != " << ase->codec_config.GetSamplingFrequencyHz(); } if (frame_duration_us == 0) { frame_duration_us = ase->codec_config.GetFrameDurationUs(); } else { LOG_ASSERT(frame_duration_us == ase->codec_config.GetFrameDurationUs()) << __func__ << " frame duration mismatch: " << +frame_duration_us << " != " << ase->codec_config.GetFrameDurationUs(); } if (octets_per_frame == 0) { octets_per_frame = *ase->codec_config.octets_per_codec_frame; } else { LOG_ASSERT(octets_per_frame == ase->codec_config.octets_per_codec_frame) << __func__ << " octets per frame mismatch: " << +octets_per_frame << " != " << *ase->codec_config.octets_per_codec_frame; const struct le_audio::stream_configuration* GetStreamSinkConfiguration( LeAudioDeviceGroup* group) { const struct le_audio::stream_configuration* stream_conf = &group->stream_conf; LOG_INFO("group_id: %d", group->group_id_); if (stream_conf->sink_streams.size() == 0) { return nullptr; } if (codec_frames_blocks_per_sdu == 0) { codec_frames_blocks_per_sdu = *ase->codec_config.codec_frames_blocks_per_sdu; } else { LOG_ASSERT(codec_frames_blocks_per_sdu == ase->codec_config.codec_frames_blocks_per_sdu) << __func__ << " codec_frames_blocks_per_sdu: " << +codec_frames_blocks_per_sdu << " != " << *ase->codec_config.codec_frames_blocks_per_sdu; } LOG(INFO) << __func__ << " Added CIS: " << +ase->cis_conn_hdl << " to stream. Allocation: " << +(*ase->codec_config.audio_channel_allocation) << " sample_freq: " << +sample_freq_hz << " frame_duration: " << +frame_duration_us << " octects per frame: " << +octets_per_frame << " codec_frame_blocks_per_sdu: " << +codec_frames_blocks_per_sdu; } } if (streams.empty()) return nullptr; if (direction == le_audio::types::kLeAudioDirectionSource) { stream_conf->source_streams = std::move(streams); stream_conf->source_sample_frequency_hz = sample_freq_hz; stream_conf->source_frame_duration_us = frame_duration_us; stream_conf->source_audio_channel_allocation = audio_channel_allocation; stream_conf->source_octets_per_codec_frame = octets_per_frame; stream_conf->source_codec_frames_blocks_per_sdu = codec_frames_blocks_per_sdu; } else if (direction == le_audio::types::kLeAudioDirectionSink) { stream_conf->sink_streams = std::move(streams); stream_conf->sink_sample_frequency_hz = sample_freq_hz; stream_conf->sink_frame_duration_us = frame_duration_us; stream_conf->sink_audio_channel_allocation = audio_channel_allocation; stream_conf->sink_octets_per_codec_frame = octets_per_frame; stream_conf->sink_codec_frames_blocks_per_sdu = codec_frames_blocks_per_sdu; } LOG(INFO) << __func__ << " configuration: " << stream_conf->conf->name; LOG_INFO("configuration: %s", stream_conf->conf->name.c_str()); return stream_conf; } struct le_audio::stream_configuration* GetStreamSinkConfiguration( LeAudioDeviceGroup* group) { return GetStreamConfigurationByDirection( group, le_audio::types::kLeAudioDirectionSink); } void OnAudioDataReady(const std::vector<uint8_t>& data) { if ((active_group_id_ == bluetooth::groups::kGroupUnknown) || (audio_sender_state_ != AudioState::STARTED)) Loading Loading @@ -2641,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); Loading @@ -2657,10 +2560,15 @@ class LeAudioClientImpl : public LeAudioClient { return true; } struct le_audio::stream_configuration* GetStreamSourceConfiguration( const struct le_audio::stream_configuration* GetStreamSourceConfiguration( LeAudioDeviceGroup* group) { return GetStreamConfigurationByDirection( group, le_audio::types::kLeAudioDirectionSource); const struct le_audio::stream_configuration* stream_conf = &group->stream_conf; if (stream_conf->source_streams.size() == 0) { return nullptr; } LOG_INFO("configuration: %s", stream_conf->conf->name.c_str()); return stream_conf; } void StartReceivingAudio(int group_id) { Loading Loading @@ -2702,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; Loading Loading @@ -3600,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) Loading
system/bta/le_audio/codec_manager.cc +2 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading
system/bta/le_audio/devices.cc +130 −0 Original line number Diff line number Diff line Loading @@ -106,9 +106,37 @@ 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; stream_conf.sink_sample_frequency_hz = 0; stream_conf.sink_codec_frames_blocks_per_sdu = 0; stream_conf.sink_octets_per_codec_frame = 0; stream_conf.sink_frame_duration_us = 0; } 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; stream_conf.source_sample_frequency_hz = 0; stream_conf.source_codec_frames_blocks_per_sdu = 0; stream_conf.source_octets_per_codec_frame = 0; stream_conf.source_frame_duration_us = 0; } void LeAudioDeviceGroup::CigClearCis(void) { LOG_INFO("group_id: %d", group_id_); cises_.clear(); ClearSinksFromConfiguration(); ClearSourcesFromConfiguration(); } void LeAudioDeviceGroup::Cleanup(void) { Loading Loading @@ -1592,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_; } Loading
system/bta/le_audio/devices.h +11 −1 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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); Loading @@ -223,6 +226,8 @@ class LeAudioDeviceGroup { bool Activate(types::LeAudioContextType context_type); void Deactivate(void); void CigClearCis(void); void ClearSinksFromConfiguration(void); void ClearSourcesFromConfiguration(void); void Cleanup(void); LeAudioDevice* GetFirstDevice(void); LeAudioDevice* GetFirstDeviceWithActiveContext( Loading Loading @@ -284,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) { Loading Loading @@ -334,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_; Loading
system/bta/le_audio/le_audio_client_test.cc +73 −0 Original line number Diff line number Diff line Loading @@ -772,6 +772,42 @@ class UnicastTestNoInit : public Test { stream_conf->source_num_of_devices++; stream_conf->source_num_of_channels += ase.codec_config.channel_count; stream_conf->source_audio_channel_allocation |= *ase.codec_config.audio_channel_allocation; if (stream_conf->source_sample_frequency_hz == 0) { stream_conf->source_sample_frequency_hz = ase.codec_config.GetSamplingFrequencyHz(); } else { ASSERT_LOG(stream_conf->source_sample_frequency_hz == ase.codec_config.GetSamplingFrequencyHz(), "sample freq mismatch: %d!=%d", stream_conf->source_sample_frequency_hz, ase.codec_config.GetSamplingFrequencyHz()); } if (stream_conf->source_octets_per_codec_frame == 0) { stream_conf->source_octets_per_codec_frame = *ase.codec_config.octets_per_codec_frame; } else { ASSERT_LOG(stream_conf->source_octets_per_codec_frame == *ase.codec_config.octets_per_codec_frame, "octets per frame mismatch: %d!=%d", stream_conf->source_octets_per_codec_frame, *ase.codec_config.octets_per_codec_frame); } if (stream_conf->source_codec_frames_blocks_per_sdu == 0) { stream_conf->source_codec_frames_blocks_per_sdu = *ase.codec_config.codec_frames_blocks_per_sdu; } else { ASSERT_LOG( stream_conf->source_codec_frames_blocks_per_sdu == *ase.codec_config.codec_frames_blocks_per_sdu, "codec_frames_blocks_per_sdu: %d!=%d", stream_conf->source_codec_frames_blocks_per_sdu, *ase.codec_config.codec_frames_blocks_per_sdu); } LOG_INFO( " Added Source Stream Configuration. CIS Connection " Loading Loading @@ -800,6 +836,43 @@ class UnicastTestNoInit : public Test { stream_conf->sink_num_of_channels += ase.codec_config.channel_count; stream_conf->sink_audio_channel_allocation |= *ase.codec_config.audio_channel_allocation; if (stream_conf->sink_sample_frequency_hz == 0) { stream_conf->sink_sample_frequency_hz = ase.codec_config.GetSamplingFrequencyHz(); } else { ASSERT_LOG(stream_conf->sink_sample_frequency_hz == ase.codec_config.GetSamplingFrequencyHz(), "sample freq mismatch: %d!=%d", stream_conf->sink_sample_frequency_hz, ase.codec_config.GetSamplingFrequencyHz()); } if (stream_conf->sink_octets_per_codec_frame == 0) { stream_conf->sink_octets_per_codec_frame = *ase.codec_config.octets_per_codec_frame; } else { ASSERT_LOG(stream_conf->sink_octets_per_codec_frame == *ase.codec_config.octets_per_codec_frame, "octets per frame mismatch: %d!=%d", stream_conf->sink_octets_per_codec_frame, *ase.codec_config.octets_per_codec_frame); } if (stream_conf->sink_codec_frames_blocks_per_sdu == 0) { stream_conf->sink_codec_frames_blocks_per_sdu = *ase.codec_config.codec_frames_blocks_per_sdu; } else { ASSERT_LOG( stream_conf->sink_codec_frames_blocks_per_sdu == *ase.codec_config.codec_frames_blocks_per_sdu, "codec_frames_blocks_per_sdu: %d!=%d", stream_conf->sink_codec_frames_blocks_per_sdu, *ase.codec_config.codec_frames_blocks_per_sdu); } LOG_INFO( " Added Sink Stream Configuration. CIS Connection " "Handle: %d" Loading