/* * Copyright 2021 HIMSA II K/S - www.himsa.com. * Represented by EHIMA - www.ehima.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "bta/include/bta_le_audio_api.h" #include "bta/include/bta_le_audio_broadcaster_api.h" #include "bta/le_audio/broadcaster/state_machine.h" #include "bta/le_audio/le_audio_types.h" #include "bta/le_audio/le_audio_utils.h" #include "device/include/controller.h" #include "embdrv/lc3/include/lc3.h" #include "gd/common/strings.h" #include "internal_include/stack_config.h" #include "osi/include/log.h" #include "osi/include/properties.h" #include "stack/include/btm_api_types.h" #include "stack/include/btm_iso_api.h" using bluetooth::common::ToString; using bluetooth::hci::IsoManager; using bluetooth::hci::iso_manager::big_create_cmpl_evt; using bluetooth::hci::iso_manager::big_terminate_cmpl_evt; using bluetooth::hci::iso_manager::BigCallbacks; using bluetooth::le_audio::BasicAudioAnnouncementData; using bluetooth::le_audio::BroadcastId; using le_audio::CodecManager; using le_audio::LeAudioCodecConfiguration; using le_audio::LeAudioSourceAudioHalClient; using le_audio::broadcaster::BigConfig; using le_audio::broadcaster::BroadcastCodecWrapper; using le_audio::broadcaster::BroadcastQosConfig; using le_audio::broadcaster::BroadcastStateMachine; using le_audio::broadcaster::BroadcastStateMachineConfig; using le_audio::broadcaster::IBroadcastStateMachineCallbacks; using le_audio::types::AudioContexts; using le_audio::types::CodecLocation; using le_audio::types::kLeAudioCodingFormatLC3; using le_audio::types::LeAudioContextType; using le_audio::types::LeAudioLtvMap; using le_audio::utils::GetAllCcids; using le_audio::utils::GetAllowedAudioContextsFromSourceMetadata; namespace { class LeAudioBroadcasterImpl; LeAudioBroadcasterImpl* instance; /* Class definitions */ /* LeAudioBroadcasterImpl class represents main implementation class for le * audio broadcaster feature in the stack. * * This class may be bonded with Test socket which allows to drive an instance * for test purposes. */ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { enum class AudioDataPathState { INACTIVE, ACTIVE, SUSPENDED, }; public: LeAudioBroadcasterImpl( bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks_) : callbacks_(callbacks_), current_phy_(PHY_LE_2M), audio_data_path_state_(AudioDataPathState::INACTIVE), le_audio_source_hal_client_(nullptr) { LOG_INFO(); /* Register State machine callbacks */ BroadcastStateMachine::Initialize(&state_machine_callbacks_); GenerateBroadcastIds(); } ~LeAudioBroadcasterImpl() override = default; void GenerateBroadcastIds(void) { btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) { if (!instance) return; /* LE Rand returns 8 octets. Lets' make 2 outstanding Broadcast Ids out * of it */ for (int i = 0; i < 8; i += 4) { BroadcastId broadcast_id = 0; /* Broadcast ID should be 3 octets long (BAP v1.0 spec.) */ STREAM_TO_UINT24(broadcast_id, rand); if (broadcast_id == bluetooth::le_audio::kBroadcastIdInvalid) continue; instance->available_broadcast_ids_.emplace_back(broadcast_id); } if (instance->available_broadcast_ids_.empty()) { LOG_ALWAYS_FATAL("Unable to generate proper broadcast identifiers."); } })); } void CleanUp() { LOG_INFO("Broadcaster"); broadcasts_.clear(); callbacks_ = nullptr; if (le_audio_source_hal_client_) { le_audio_source_hal_client_->Stop(); le_audio_source_hal_client_.reset(); } } void Stop() { LOG_INFO("Broadcaster"); for (auto& sm_pair : broadcasts_) { StopAudioBroadcast(sm_pair.first); } } static BasicAudioAnnouncementData prepareAnnouncement( const BroadcastCodecWrapper& codec_config, LeAudioLtvMap metadata) { BasicAudioAnnouncementData announcement; /* Prepare the announcement */ announcement.presentation_delay = 0x004E20; /* TODO: Use the proper value */ auto const& codec_id = codec_config.GetLeAudioCodecId(); /* Note: Currently we have a single audio source configured with a one * set of codec/pcm parameters thus we can use a single subgroup * for all the BISes. Configure common BIS codec params at the * subgroup level. */ announcement.subgroup_configs = {{ .codec_config = { .codec_id = codec_id.coding_format, .vendor_company_id = codec_id.vendor_company_id, .vendor_codec_id = codec_id.vendor_codec_id, .codec_specific_params = codec_config.GetSubgroupCodecSpecData().Values(), }, .metadata = metadata.Values(), .bis_configs = {}, }}; /* BIS indices range is [1-31] - BASS, Sec.3.2 Broadcast Receive State. */ for (uint8_t i = 0; i < codec_config.GetNumChannels(); ++i) { announcement.subgroup_configs[0].bis_configs.push_back( {.codec_specific_params = codec_config.GetBisCodecSpecData(i + 1).Values(), .bis_index = static_cast(i + 1)}); } return announcement; } void UpdateStreamingContextTypeOnAllSubgroups(const AudioContexts& contexts) { LOG_DEBUG("%s context_type_map=%s", __func__, contexts.to_string().c_str()); auto ccids = GetAllCcids(contexts); if (ccids.empty()) { LOG_WARN("%s No content providers available for context_type_map=%s.", __func__, contexts.to_string().c_str()); } std::vector stream_context_vec(2); auto pp = stream_context_vec.data(); UINT16_TO_STREAM(pp, contexts.value()); for (auto const& kv_it : broadcasts_) { auto& broadcast = kv_it.second; if (broadcast->GetState() == BroadcastStateMachine::State::STREAMING) { auto announcement = broadcast->GetBroadcastAnnouncement(); bool broadcast_update = false; // Replace context type and CCID list for (auto& subgroup : announcement.subgroup_configs) { auto subgroup_ltv = LeAudioLtvMap(subgroup.metadata); bool subgroup_update = false; auto existing_context = subgroup_ltv.Find( le_audio::types::kLeAudioMetadataTypeStreamingAudioContext); if (existing_context) { if (memcmp(stream_context_vec.data(), existing_context->data(), existing_context->size()) != 0) { subgroup_ltv.Add( le_audio::types::kLeAudioMetadataTypeStreamingAudioContext, stream_context_vec); subgroup_update = true; } } else { subgroup_ltv.Add( le_audio::types::kLeAudioMetadataTypeStreamingAudioContext, stream_context_vec); subgroup_update = true; } auto existing_ccid_list = subgroup_ltv.Find(le_audio::types::kLeAudioMetadataTypeCcidList); if (existing_ccid_list) { if (ccids.empty()) { subgroup_ltv.Remove( le_audio::types::kLeAudioMetadataTypeCcidList); subgroup_update = true; } else if (!std::is_permutation(ccids.begin(), ccids.end(), existing_ccid_list->begin())) { subgroup_ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList, ccids); subgroup_update = true; } } else if (!ccids.empty()) { subgroup_ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList, ccids); subgroup_update = true; } if (subgroup_update) { subgroup.metadata = subgroup_ltv.Values(); broadcast_update = true; } } if (broadcast_update) { broadcast->UpdateBroadcastAnnouncement(std::move(announcement)); } } } } void UpdateMetadata(uint32_t broadcast_id, std::vector metadata) override { if (broadcasts_.count(broadcast_id) == 0) { LOG_ERROR("No such broadcast_id=%d", broadcast_id); return; } LOG_INFO("For broadcast_id=%d", broadcast_id); auto& codec_config = broadcasts_[broadcast_id]->GetCodecConfig(); /* Prepare the announcement format */ bool is_metadata_valid; auto ltv = LeAudioLtvMap::Parse(metadata.data(), metadata.size(), is_metadata_valid); if (!is_metadata_valid) { LOG_ERROR("Invalid metadata provided."); return; } auto context_type = AudioContexts(LeAudioContextType::MEDIA); /* Adds multiple contexts and CCIDs regardless of the incoming audio * context. Android has only two CCIDs, one for Media and one for * Conversational context. Even though we are not broadcasting * Conversational streams, some PTS test cases wants multiple CCIDs. */ if (stack_config_get_interface() ->get_pts_force_le_audio_multiple_contexts_metadata()) { context_type = LeAudioContextType::MEDIA | LeAudioContextType::CONVERSATIONAL; auto stream_context_vec = ltv.Find(le_audio::types::kLeAudioMetadataTypeStreamingAudioContext); if (stream_context_vec) { auto pp = stream_context_vec.value().data(); UINT16_TO_STREAM(pp, context_type.value()); } } auto stream_context_vec = ltv.Find(le_audio::types::kLeAudioMetadataTypeStreamingAudioContext); if (stream_context_vec) { auto pp = stream_context_vec.value().data(); STREAM_TO_UINT16(context_type.value_ref(), pp); } // Append the CCID list auto ccid_vec = GetAllCcids(context_type); if (!ccid_vec.empty()) { ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList, ccid_vec); } BasicAudioAnnouncementData announcement = prepareAnnouncement(codec_config, std::move(ltv)); broadcasts_[broadcast_id]->UpdateBroadcastAnnouncement( std::move(announcement)); } void CreateAudioBroadcast(std::vector metadata, std::optional broadcast_code) override { auto broadcast_id = available_broadcast_ids_.back(); available_broadcast_ids_.pop_back(); if (available_broadcast_ids_.size() == 0) GenerateBroadcastIds(); /* Prepare the announcement format */ bool is_metadata_valid; auto ltv = LeAudioLtvMap::Parse(metadata.data(), metadata.size(), is_metadata_valid); if (!is_metadata_valid) { LOG_ERROR("Invalid metadata provided."); return; } auto context_type = AudioContexts(LeAudioContextType::MEDIA); /* Adds multiple contexts and CCIDs regardless of the incoming audio * context. Android has only two CCIDs, one for Media and one for * Conversational context. Even though we are not broadcasting * Conversational streams, some PTS test cases wants multiple CCIDs. */ if (stack_config_get_interface() ->get_pts_force_le_audio_multiple_contexts_metadata()) { context_type = LeAudioContextType::MEDIA | LeAudioContextType::CONVERSATIONAL; auto stream_context_vec = ltv.Find(le_audio::types::kLeAudioMetadataTypeStreamingAudioContext); if (stream_context_vec) { auto pp = stream_context_vec.value().data(); UINT16_TO_STREAM(pp, context_type.value()); } } auto stream_context_vec = ltv.Find(le_audio::types::kLeAudioMetadataTypeStreamingAudioContext); if (stream_context_vec) { auto pp = stream_context_vec.value().data(); STREAM_TO_UINT16(context_type.value_ref(), pp); } // Append the CCID list auto ccid_vec = GetAllCcids(context_type); if (!ccid_vec.empty()) { ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList, ccid_vec); } if (CodecManager::GetInstance()->GetCodecLocation() == CodecLocation::ADSP) { auto offload_config = CodecManager::GetInstance()->GetBroadcastOffloadConfig(); BroadcastCodecWrapper codec_config( {.coding_format = le_audio::types::kLeAudioCodingFormatLC3, .vendor_company_id = le_audio::types::kLeAudioVendorCompanyIdUndefined, .vendor_codec_id = le_audio::types::kLeAudioVendorCodecIdUndefined}, {.num_channels = static_cast(offload_config->stream_map.size()), .sample_rate = offload_config->sampling_rate, .bits_per_sample = offload_config->bits_per_sample, .data_interval_us = offload_config->frame_duration}, offload_config->codec_bitrate, offload_config->octets_per_frame); BroadcastQosConfig qos_config(offload_config->retransmission_number, offload_config->max_transport_latency); BroadcastStateMachineConfig msg = { .broadcast_id = broadcast_id, .streaming_phy = GetStreamingPhy(), .codec_wrapper = codec_config, .qos_config = qos_config, .announcement = prepareAnnouncement(codec_config, std::move(ltv)), .broadcast_code = std::move(broadcast_code)}; pending_broadcasts_.push_back( std::move(BroadcastStateMachine::CreateInstance(std::move(msg)))); } else { auto codec_qos_pair = le_audio::broadcaster::getStreamConfigForContext(context_type); BroadcastStateMachineConfig msg = { .broadcast_id = broadcast_id, .streaming_phy = GetStreamingPhy(), .codec_wrapper = codec_qos_pair.first, .qos_config = codec_qos_pair.second, .announcement = prepareAnnouncement(codec_qos_pair.first, std::move(ltv)), .broadcast_code = std::move(broadcast_code)}; /* Create the broadcaster instance - we'll receive it's init state in the * async callback */ pending_broadcasts_.push_back( std::move(BroadcastStateMachine::CreateInstance(std::move(msg)))); } LOG_INFO("CreateAudioBroadcast"); // Notify the error instead just fail silently if (!pending_broadcasts_.back()->Initialize()) { pending_broadcasts_.pop_back(); callbacks_->OnBroadcastCreated(bluetooth::le_audio::kBroadcastIdInvalid, false); } } void SuspendAudioBroadcast(uint32_t broadcast_id) override { LOG_INFO("broadcast_id=%d", broadcast_id); if (broadcasts_.count(broadcast_id) != 0) { LOG_INFO("Stopping AudioHalClient"); if (le_audio_source_hal_client_) le_audio_source_hal_client_->Stop(); broadcasts_[broadcast_id]->SetMuted(true); broadcasts_[broadcast_id]->ProcessMessage( BroadcastStateMachine::Message::SUSPEND, nullptr); } else { LOG_ERROR("No such broadcast_id=%d", broadcast_id); } } static bool IsAnyoneStreaming() { if (!instance) return false; auto const& iter = std::find_if(instance->broadcasts_.cbegin(), instance->broadcasts_.cend(), [](auto const& sm) { return sm.second->GetState() == BroadcastStateMachine::State::STREAMING; }); return (iter != instance->broadcasts_.cend()); } void StartAudioBroadcast(uint32_t broadcast_id) override { LOG_INFO("Starting broadcast_id=%d", broadcast_id); if (IsAnyoneStreaming()) { LOG_ERROR("Stop the other broadcast first!"); return; } if (broadcasts_.count(broadcast_id) != 0) { if (!le_audio_source_hal_client_) { le_audio_source_hal_client_ = LeAudioSourceAudioHalClient::AcquireBroadcast(); if (!le_audio_source_hal_client_) { LOG_ERROR("Could not acquire le audio"); return; } } broadcasts_[broadcast_id]->ProcessMessage( BroadcastStateMachine::Message::START, nullptr); } else { LOG_ERROR("No such broadcast_id=%d", broadcast_id); } } void StopAudioBroadcast(uint32_t broadcast_id) override { if (broadcasts_.count(broadcast_id) == 0) { LOG_ERROR("no such broadcast_id=%d", broadcast_id); return; } LOG_INFO("Stopping AudioHalClient, broadcast_id=%d", broadcast_id); if (le_audio_source_hal_client_) le_audio_source_hal_client_->Stop(); broadcasts_[broadcast_id]->SetMuted(true); broadcasts_[broadcast_id]->ProcessMessage( BroadcastStateMachine::Message::STOP, nullptr); } void DestroyAudioBroadcast(uint32_t broadcast_id) override { LOG_INFO("Destroying broadcast_id=%d", broadcast_id); broadcasts_.erase(broadcast_id); } std::optional GetBroadcastMetadataOpt( bluetooth::le_audio::BroadcastId broadcast_id) { bluetooth::le_audio::BroadcastMetadata metadata; for (auto const& kv_it : broadcasts_) { if (kv_it.second->GetBroadcastId() == broadcast_id) { metadata.broadcast_id = kv_it.second->GetBroadcastId(); metadata.adv_sid = kv_it.second->GetAdvertisingSid(); metadata.pa_interval = kv_it.second->GetPaInterval(); metadata.addr = kv_it.second->GetOwnAddress(); metadata.addr_type = kv_it.second->GetOwnAddressType(); metadata.broadcast_code = kv_it.second->GetBroadcastCode(); metadata.basic_audio_announcement = kv_it.second->GetBroadcastAnnouncement(); return metadata; } } return std::nullopt; } void GetBroadcastMetadata(uint32_t broadcast_id) override { if (broadcasts_.count(broadcast_id) == 0) { LOG_ERROR("No such broadcast_id=%d", broadcast_id); return; } auto meta = GetBroadcastMetadataOpt(broadcast_id); if (!meta) { LOG_ERROR("No metadata for broadcast_id=%d", broadcast_id); return; } callbacks_->OnBroadcastMetadataChanged(broadcast_id, std::move(meta.value())); } void GetAllBroadcastStates(void) override { for (auto const& kv_it : broadcasts_) { callbacks_->OnBroadcastStateChanged( kv_it.second->GetBroadcastId(), static_cast( kv_it.second->GetState())); } } void IsValidBroadcast( uint32_t broadcast_id, uint8_t addr_type, RawAddress addr, base::Callback cb) override { if (broadcasts_.count(broadcast_id) == 0) { LOG_ERROR("No such broadcast_id=%d", broadcast_id); std::move(cb).Run(broadcast_id, addr_type, addr, false); return; } broadcasts_[broadcast_id]->RequestOwnAddress(base::Bind( [](uint32_t broadcast_id, uint8_t req_address_type, RawAddress req_address, base::Callback cb, uint8_t rcv_address_type, RawAddress rcv_address) { bool is_local = (req_address_type == rcv_address_type) && (req_address == rcv_address); std::move(cb).Run(broadcast_id, req_address_type, req_address, is_local); }, broadcast_id, addr_type, addr, std::move(cb))); } void SetStreamingPhy(uint8_t phy) override { current_phy_ = phy; } uint8_t GetStreamingPhy(void) const override { return current_phy_; } BroadcastId BroadcastIdFromBigHandle(uint8_t big_handle) const { auto pair_it = std::find_if(broadcasts_.begin(), broadcasts_.end(), [big_handle](auto const& entry) { return entry.second->GetAdvertisingSid() == big_handle; }); if (pair_it != broadcasts_.end()) { return pair_it->second->GetBroadcastId(); } return bluetooth::le_audio::kBroadcastIdInvalid; } void OnSetupIsoDataPath(uint8_t status, uint16_t conn_handle, uint8_t big_handle) override { auto broadcast_id = BroadcastIdFromBigHandle(big_handle); CHECK(broadcasts_.count(broadcast_id) != 0); broadcasts_[broadcast_id]->OnSetupIsoDataPath(status, conn_handle); } void OnRemoveIsoDataPath(uint8_t status, uint16_t conn_handle, uint8_t big_handle) override { auto broadcast_id = BroadcastIdFromBigHandle(big_handle); CHECK(broadcasts_.count(broadcast_id) != 0); broadcasts_[broadcast_id]->OnRemoveIsoDataPath(status, conn_handle); } void OnBigEvent(uint8_t event, void* data) override { switch (event) { case bluetooth::hci::iso_manager::kIsoEventBigOnCreateCmpl: { auto* evt = static_cast(data); auto broadcast_id = BroadcastIdFromBigHandle(evt->big_id); CHECK(broadcasts_.count(broadcast_id) != 0); broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_CREATE_BIG_CPL_EVT, evt); } break; case bluetooth::hci::iso_manager::kIsoEventBigOnTerminateCmpl: { auto* evt = static_cast(data); auto broadcast_id = BroadcastIdFromBigHandle(evt->big_id); CHECK(broadcasts_.count(broadcast_id) != 0); broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, evt); le_audio_source_hal_client_.reset(); } break; default: LOG_ERROR("Invalid event=%d", event); } } void Dump(int fd) { std::stringstream stream; stream << " Number of broadcasts: " << broadcasts_.size() << "\n"; for (auto& broadcast_pair : broadcasts_) { auto& broadcast = broadcast_pair.second; if (broadcast) stream << *broadcast; } dprintf(fd, "%s", stream.str().c_str()); } private: static class BroadcastStateMachineCallbacks : public IBroadcastStateMachineCallbacks { void OnStateMachineCreateStatus(uint32_t broadcast_id, bool initialized) override { auto pending_broadcast = std::find_if( instance->pending_broadcasts_.begin(), instance->pending_broadcasts_.end(), [broadcast_id](auto& sm) { return (sm->GetBroadcastId() == broadcast_id); }); LOG_ASSERT(pending_broadcast != instance->pending_broadcasts_.end()); LOG_ASSERT(instance->broadcasts_.count(broadcast_id) == 0); if (initialized) { const uint32_t broadcast_id = (*pending_broadcast)->GetBroadcastId(); LOG_INFO("broadcast_id=%d state=%s", broadcast_id, ToString((*pending_broadcast)->GetState()).c_str()); instance->broadcasts_[broadcast_id] = std::move(*pending_broadcast); } else { LOG_ERROR("Failed creating broadcast!"); } instance->pending_broadcasts_.erase(pending_broadcast); instance->callbacks_->OnBroadcastCreated(broadcast_id, initialized); } void OnStateMachineDestroyed(uint32_t broadcast_id) override { /* This is a special case when state machine destructor calls this * callback. It may happen during the Cleanup() call when all state * machines are erased and instance can already be set to null to avoid * unnecessary calls. */ if (instance) instance->callbacks_->OnBroadcastDestroyed(broadcast_id); } static int getStreamerCount() { return std::count_if(instance->broadcasts_.begin(), instance->broadcasts_.end(), [](auto const& sm) { LOG_VERBOSE( "broadcast_id=%d, state=%s", sm.second->GetBroadcastId(), ToString(sm.second->GetState()).c_str()); return sm.second->GetState() == BroadcastStateMachine::State::STREAMING; }); } void OnStateMachineEvent(uint32_t broadcast_id, BroadcastStateMachine::State state, const void* data) override { LOG_INFO("broadcast_id=%d state=%s", broadcast_id, ToString(state).c_str()); switch (state) { case BroadcastStateMachine::State::STOPPED: /* Pass through */ case BroadcastStateMachine::State::CONFIGURING: /* Pass through */ case BroadcastStateMachine::State::CONFIGURED: /* Pass through */ case BroadcastStateMachine::State::STOPPING: /* Nothing to do here? */ break; case BroadcastStateMachine::State::STREAMING: if (getStreamerCount() == 1) { LOG_INFO("Starting AudioHalClient"); if (instance->broadcasts_.count(broadcast_id) != 0) { const auto& broadcast = instance->broadcasts_.at(broadcast_id); // Reconfigure encoder instance for the new stream requirements audio_receiver_.setCurrentCodecConfig( broadcast->GetCodecConfig()); audio_receiver_.CheckAndReconfigureEncoders(); broadcast->SetMuted(false); auto cfg = static_cast(data); auto is_started = instance->le_audio_source_hal_client_->Start( *cfg, &audio_receiver_); if (!is_started) { /* Audio Source setup failed - stop the broadcast */ instance->StopAudioBroadcast(broadcast_id); return; } instance->audio_data_path_state_ = AudioDataPathState::ACTIVE; } } break; }; instance->callbacks_->OnBroadcastStateChanged( broadcast_id, static_cast(state)); } void OnOwnAddressResponse(uint32_t broadcast_id, uint8_t addr_type, RawAddress addr) override { /* Not used currently */ } void OnBigCreated(const std::vector& conn_handle) { CodecManager::GetInstance()->UpdateBroadcastConnHandle( conn_handle, std::bind( &LeAudioSourceAudioHalClient::UpdateBroadcastAudioConfigToHal, instance->le_audio_source_hal_client_.get(), std::placeholders::_1)); } } state_machine_callbacks_; static class LeAudioSourceCallbacksImpl : public LeAudioSourceAudioHalClient::Callbacks { public: LeAudioSourceCallbacksImpl() : codec_wrapper_(le_audio::broadcaster::getStreamConfigForContext( AudioContexts(LeAudioContextType::UNSPECIFIED)) .first) {} void CheckAndReconfigureEncoders() { auto const& codec_id = codec_wrapper_.GetLeAudioCodecId(); if (codec_id.coding_format != kLeAudioCodingFormatLC3) { LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id.coding_format, codec_id.vendor_company_id, codec_id.vendor_codec_id); return; } if (enc_audio_buffers_.size() != codec_wrapper_.GetNumChannels()) { enc_audio_buffers_.resize(codec_wrapper_.GetNumChannels()); } const int dt_us = codec_wrapper_.GetDataIntervalUs(); const int sr_hz = codec_wrapper_.GetSampleRate(); const auto encoder_bytes = lc3_encoder_size(dt_us, sr_hz); const auto channel_bytes = codec_wrapper_.GetMaxSduSizePerChannel(); /* TODO: We should act smart and reuse current configurations */ encoders_.clear(); encoders_mem_.clear(); while (encoders_.size() < codec_wrapper_.GetNumChannels()) { auto& encoder_buf = enc_audio_buffers_.at(encoders_.size()); encoder_buf.resize(channel_bytes); encoders_mem_.emplace_back(malloc(encoder_bytes), &std::free); encoders_.emplace_back( lc3_setup_encoder(dt_us, sr_hz, 0, encoders_mem_.back().get())); } } const BroadcastCodecWrapper& getCurrentCodecConfig(void) const { return codec_wrapper_; } void setCurrentCodecConfig(BroadcastCodecWrapper const& config) { codec_wrapper_ = config; } void encodeLc3Channel(lc3_encoder_t encoder, std::vector& out_buffer, const std::vector& data, int initial_channel_offset, int pitch_samples, int num_channels) { auto encoder_status = lc3_encode(encoder, LC3_PCM_FORMAT_S16, (int16_t*)(data.data() + initial_channel_offset), pitch_samples, out_buffer.size(), out_buffer.data()); if (encoder_status != 0) { LOG_ERROR("Encoding error=%d", encoder_status); } } static void sendBroadcastData( const std::unique_ptr& broadcast, std::vector>& encoded_channels) { auto const& config = broadcast->GetBigConfig(); if (config == std::nullopt) { LOG_ERROR( "Broadcast broadcast_id=%d has no valid BIS configurations in " "state=%s", broadcast->GetBroadcastId(), ToString(broadcast->GetState()).c_str()); return; } if (config->connection_handles.size() < encoded_channels.size()) { LOG_ERROR("Not enough BIS'es to broadcast all channels!"); return; } for (uint8_t chan = 0; chan < encoded_channels.size(); ++chan) { IsoManager::GetInstance()->SendIsoData(config->connection_handles[chan], encoded_channels[chan].data(), encoded_channels[chan].size()); } } virtual void OnAudioDataReady(const std::vector& data) override { if (!instance) return; LOG_VERBOSE("Received %zu bytes.", data.size()); /* Constants for the channel data configuration */ const auto num_channels = codec_wrapper_.GetNumChannels(); const auto bytes_per_sample = (codec_wrapper_.GetBitsPerSample() / 8); /* Prepare encoded data for all channels */ for (uint8_t chan = 0; chan < num_channels; ++chan) { /* TODO: Use encoder agnostic wrapper */ encodeLc3Channel(encoders_[chan], enc_audio_buffers_[chan], data, chan * bytes_per_sample, num_channels, num_channels); } /* Currently there is no way to broadcast multiple distinct streams. * We just receive all system sounds mixed into a one stream and each * broadcast gets the same data. */ for (auto& broadcast_pair : instance->broadcasts_) { auto& broadcast = broadcast_pair.second; if ((broadcast->GetState() == BroadcastStateMachine::State::STREAMING) && !broadcast->IsMuted()) sendBroadcastData(broadcast, enc_audio_buffers_); } LOG_VERBOSE("All data sent."); } virtual void OnAudioSuspend( std::promise do_suspend_promise) override { LOG_INFO(); /* TODO: Should we suspend all broadcasts - remove BIGs? */ do_suspend_promise.set_value(); if (instance) instance->audio_data_path_state_ = AudioDataPathState::SUSPENDED; } virtual void OnAudioResume(void) override { LOG_INFO(); if (!instance) return; /* TODO: Should we resume all broadcasts - recreate BIGs? */ instance->audio_data_path_state_ = AudioDataPathState::ACTIVE; if (!IsAnyoneStreaming()) { instance->le_audio_source_hal_client_->CancelStreamingRequest(); return; } instance->le_audio_source_hal_client_->ConfirmStreamingRequest(); } virtual void OnAudioMetadataUpdate( std::vector source_metadata) override { LOG_INFO(); if (!instance) return; /* TODO: Should we take supported contexts from ASCS? */ auto supported_context_types = le_audio::types::kLeAudioContextAllTypes; auto contexts = GetAllowedAudioContextsFromSourceMetadata( source_metadata, supported_context_types); if (contexts.any()) { /* NOTICE: We probably don't want to change the stream configuration * on each metadata change, so just update the context type metadata. * Since we are not able to identify individual track streams and * they are all mixed inside a single data stream, we will update * the metadata of all BIS subgroups with the same combined context. */ instance->UpdateStreamingContextTypeOnAllSubgroups(contexts); } } private: BroadcastCodecWrapper codec_wrapper_; std::vector encoders_; std::vector> encoders_mem_; std::vector> enc_audio_buffers_; } audio_receiver_; bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks_; std::map> broadcasts_; std::vector> pending_broadcasts_; /* Some BIG params are set globally */ uint8_t current_phy_; AudioDataPathState audio_data_path_state_; std::unique_ptr le_audio_source_hal_client_; std::vector available_broadcast_ids_; }; /* Static members definitions */ LeAudioBroadcasterImpl::BroadcastStateMachineCallbacks LeAudioBroadcasterImpl::state_machine_callbacks_; LeAudioBroadcasterImpl::LeAudioSourceCallbacksImpl LeAudioBroadcasterImpl::audio_receiver_; } /* namespace */ void LeAudioBroadcaster::Initialize( bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks, base::Callback audio_hal_verifier) { LOG_INFO(); if (instance) { LOG_ERROR("Already initialized"); return; } if (!controller_get_interface()->supports_ble_isochronous_broadcaster() && !osi_property_get_bool("persist.bluetooth.fake_iso_support", false)) { LOG_WARN("Isochronous Broadcast not supported by the controller!"); return; } if (!std::move(audio_hal_verifier).Run()) { LOG_ALWAYS_FATAL("HAL requirements not met. Init aborted."); } IsoManager::GetInstance()->Start(); instance = new LeAudioBroadcasterImpl(callbacks); /* Register HCI event handlers */ IsoManager::GetInstance()->RegisterBigCallbacks(instance); } bool LeAudioBroadcaster::IsLeAudioBroadcasterRunning() { return instance; } LeAudioBroadcaster* LeAudioBroadcaster::Get(void) { LOG_INFO(); CHECK(instance); return instance; } void LeAudioBroadcaster::Stop(void) { LOG_INFO(); if (instance) { instance->Stop(); } } void LeAudioBroadcaster::Cleanup(void) { LOG_INFO(); if (instance == nullptr) return; LeAudioBroadcasterImpl* ptr = instance; instance = nullptr; ptr->CleanUp(); delete ptr; } void LeAudioBroadcaster::DebugDump(int fd) { dprintf(fd, "Le Audio Broadcaster:\n"); if (instance) instance->Dump(fd); dprintf(fd, "\n"); }