| /* |
| * 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. |
| */ |
| |
| #define LOG_TAG "BTAudioClientLeAudio" |
| |
| #include "le_audio_software.h" |
| |
| #include "client_interface.h" |
| #include "osi/include/log.h" |
| #include "osi/include/properties.h" |
| |
| namespace { |
| |
| using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample; |
| using ::android::hardware::bluetooth::audio::V2_0::ChannelMode; |
| using ::android::hardware::bluetooth::audio::V2_0::CodecType; |
| using ::android::hardware::bluetooth::audio::V2_1::PcmParameters; |
| using ::bluetooth::audio::BluetoothAudioCtrlAck; |
| using ::bluetooth::audio::SampleRate_2_1; |
| using ::bluetooth::audio::SessionType; |
| using ::bluetooth::audio::SessionType_2_1; |
| using ::bluetooth::audio::le_audio::LeAudioClientInterface; |
| using ::bluetooth::audio::le_audio::StreamCallbacks; |
| |
| bluetooth::audio::BluetoothAudioSinkClientInterface* |
| le_audio_sink_hal_clientinterface = nullptr; |
| bluetooth::audio::BluetoothAudioSourceClientInterface* |
| le_audio_source_hal_clientinterface = nullptr; |
| |
| static bool is_source_hal_enabled() { |
| return le_audio_source_hal_clientinterface != nullptr; |
| } |
| |
| static bool is_sink_hal_enabled() { |
| return le_audio_sink_hal_clientinterface != nullptr; |
| } |
| |
| class LeAudioTransport { |
| public: |
| LeAudioTransport(void (*flush)(void), StreamCallbacks stream_cb, |
| PcmParameters pcm_config) |
| : flush_(std::move(flush)), |
| stream_cb_(std::move(stream_cb)), |
| remote_delay_report_ms_(0), |
| total_bytes_processed_(0), |
| data_position_({}), |
| pcm_config_(std::move(pcm_config)), |
| is_pending_start_request_(false){}; |
| |
| BluetoothAudioCtrlAck StartRequest() { |
| LOG(INFO) << __func__; |
| |
| /* |
| * Set successful state as initial, it may be overwritten with cancelation |
| * during on_resume handling |
| */ |
| pending_start_request_state_ = BluetoothAudioCtrlAck::PENDING; |
| |
| if (stream_cb_.on_resume_(true)) { |
| if (pending_start_request_state_ == BluetoothAudioCtrlAck::PENDING) |
| is_pending_start_request_ = true; |
| |
| return pending_start_request_state_; |
| } |
| |
| return BluetoothAudioCtrlAck::FAILURE; |
| } |
| |
| BluetoothAudioCtrlAck SuspendRequest() { |
| LOG(INFO) << __func__; |
| if (stream_cb_.on_suspend_()) { |
| flush_(); |
| return BluetoothAudioCtrlAck::SUCCESS_FINISHED; |
| } else { |
| return BluetoothAudioCtrlAck::FAILURE; |
| } |
| } |
| |
| void StopRequest() { |
| LOG(INFO) << __func__; |
| if (stream_cb_.on_suspend_()) { |
| flush_(); |
| } |
| } |
| |
| bool GetPresentationPosition(uint64_t* remote_delay_report_ns, |
| uint64_t* total_bytes_processed, |
| timespec* data_position) { |
| VLOG(2) << __func__ << ": data=" << total_bytes_processed_ |
| << " byte(s), timestamp=" << data_position_.tv_sec << "." |
| << data_position_.tv_nsec |
| << "s, delay report=" << remote_delay_report_ms_ << " msec."; |
| if (remote_delay_report_ns != nullptr) { |
| *remote_delay_report_ns = remote_delay_report_ms_ * 1000000u; |
| } |
| if (total_bytes_processed != nullptr) |
| *total_bytes_processed = total_bytes_processed_; |
| if (data_position != nullptr) *data_position = data_position_; |
| |
| return true; |
| } |
| |
| void MetadataChanged(const source_metadata_t& source_metadata) { |
| auto track_count = source_metadata.track_count; |
| |
| if (track_count == 0) { |
| LOG(WARNING) << ", invalid number of metadata changed tracks"; |
| return; |
| } |
| |
| stream_cb_.on_metadata_update_(source_metadata); |
| } |
| |
| void ResetPresentationPosition() { |
| VLOG(2) << __func__ << ": called."; |
| remote_delay_report_ms_ = 0; |
| total_bytes_processed_ = 0; |
| data_position_ = {}; |
| } |
| |
| void LogBytesProcessed(size_t bytes_processed) { |
| if (bytes_processed) { |
| total_bytes_processed_ += bytes_processed; |
| clock_gettime(CLOCK_MONOTONIC, &data_position_); |
| } |
| } |
| |
| void SetRemoteDelay(uint16_t delay_report_ms) { |
| LOG(INFO) << __func__ << ": delay_report=" << delay_report_ms << " msec"; |
| remote_delay_report_ms_ = delay_report_ms; |
| } |
| |
| const PcmParameters& LeAudioGetSelectedHalPcmConfig() { return pcm_config_; } |
| |
| void LeAudioSetSelectedHalPcmConfig(SampleRate_2_1 sample_rate, |
| BitsPerSample bit_rate, |
| ChannelMode channel_mode, |
| uint32_t data_interval) { |
| pcm_config_.sampleRate = sample_rate; |
| pcm_config_.bitsPerSample = bit_rate; |
| pcm_config_.channelMode = channel_mode; |
| pcm_config_.dataIntervalUs = data_interval; |
| } |
| |
| bool SetAudioCtrlAckStateAndResetPendingStartStreamFlag( |
| BluetoothAudioCtrlAck state) { |
| bool ret = is_pending_start_request_; |
| is_pending_start_request_ = false; |
| pending_start_request_state_ = state; |
| |
| return ret; |
| } |
| |
| private: |
| void (*flush_)(void); |
| StreamCallbacks stream_cb_; |
| uint16_t remote_delay_report_ms_; |
| uint64_t total_bytes_processed_; |
| timespec data_position_; |
| PcmParameters pcm_config_; |
| bool is_pending_start_request_; |
| BluetoothAudioCtrlAck pending_start_request_state_; |
| }; |
| |
| static void flush_sink() { |
| if (!is_sink_hal_enabled()) return; |
| |
| le_audio_sink_hal_clientinterface->FlushAudioData(); |
| } |
| |
| // Sink transport implementation for Le Audio |
| class LeAudioSinkTransport |
| : public bluetooth::audio::IBluetoothSinkTransportInstance { |
| public: |
| LeAudioSinkTransport(StreamCallbacks stream_cb) |
| : IBluetoothSinkTransportInstance( |
| SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH, {}) { |
| transport_ = |
| new LeAudioTransport(flush_sink, std::move(stream_cb), |
| {SampleRate_2_1::RATE_16000, ChannelMode::STEREO, |
| BitsPerSample::BITS_16, 0}); |
| }; |
| |
| ~LeAudioSinkTransport() { delete transport_; } |
| |
| BluetoothAudioCtrlAck StartRequest() override { |
| return transport_->StartRequest(); |
| } |
| |
| BluetoothAudioCtrlAck SuspendRequest() override { |
| return transport_->SuspendRequest(); |
| } |
| |
| void StopRequest() override { transport_->StopRequest(); } |
| |
| bool GetPresentationPosition(uint64_t* remote_delay_report_ns, |
| uint64_t* total_bytes_read, |
| timespec* data_position) override { |
| return transport_->GetPresentationPosition(remote_delay_report_ns, |
| total_bytes_read, data_position); |
| } |
| |
| void MetadataChanged(const source_metadata_t& source_metadata) override { |
| transport_->MetadataChanged(source_metadata); |
| } |
| |
| void ResetPresentationPosition() override { |
| transport_->ResetPresentationPosition(); |
| } |
| |
| void LogBytesRead(size_t bytes_read) override { |
| transport_->LogBytesProcessed(bytes_read); |
| } |
| |
| void SetRemoteDelay(uint16_t delay_report_ms) { |
| transport_->SetRemoteDelay(delay_report_ms); |
| } |
| |
| const PcmParameters& LeAudioGetSelectedHalPcmConfig() { |
| return transport_->LeAudioGetSelectedHalPcmConfig(); |
| } |
| |
| void LeAudioSetSelectedHalPcmConfig(SampleRate_2_1 sample_rate, |
| BitsPerSample bit_rate, |
| ChannelMode channel_mode, |
| uint32_t data_interval) { |
| transport_->LeAudioSetSelectedHalPcmConfig(sample_rate, bit_rate, |
| channel_mode, data_interval); |
| } |
| |
| bool SetAudioCtrlAckStateAndResetPendingStartStreamFlag( |
| BluetoothAudioCtrlAck state = BluetoothAudioCtrlAck::PENDING) { |
| return transport_->SetAudioCtrlAckStateAndResetPendingStartStreamFlag( |
| state); |
| } |
| |
| private: |
| LeAudioTransport* transport_; |
| }; |
| |
| static void flush_source() { |
| if (le_audio_source_hal_clientinterface == nullptr) return; |
| |
| le_audio_source_hal_clientinterface->FlushAudioData(); |
| } |
| |
| class LeAudioSourceTransport |
| : public bluetooth::audio::IBluetoothSourceTransportInstance { |
| public: |
| LeAudioSourceTransport(StreamCallbacks stream_cb) |
| : IBluetoothSourceTransportInstance( |
| SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH, {}) { |
| transport_ = |
| new LeAudioTransport(flush_source, std::move(stream_cb), |
| {SampleRate_2_1::RATE_16000, ChannelMode::MONO, |
| BitsPerSample::BITS_16, 0}); |
| }; |
| |
| ~LeAudioSourceTransport() { delete transport_; } |
| |
| BluetoothAudioCtrlAck StartRequest() override { |
| return transport_->StartRequest(); |
| } |
| |
| BluetoothAudioCtrlAck SuspendRequest() override { |
| return transport_->SuspendRequest(); |
| } |
| |
| void StopRequest() override { transport_->StopRequest(); } |
| |
| bool GetPresentationPosition(uint64_t* remote_delay_report_ns, |
| uint64_t* total_bytes_written, |
| timespec* data_position) override { |
| return transport_->GetPresentationPosition( |
| remote_delay_report_ns, total_bytes_written, data_position); |
| } |
| |
| void MetadataChanged(const source_metadata_t& source_metadata) override { |
| transport_->MetadataChanged(source_metadata); |
| } |
| |
| void ResetPresentationPosition() override { |
| transport_->ResetPresentationPosition(); |
| } |
| |
| void LogBytesWritten(size_t bytes_written) override { |
| transport_->LogBytesProcessed(bytes_written); |
| } |
| |
| void SetRemoteDelay(uint16_t delay_report_ms) { |
| transport_->SetRemoteDelay(delay_report_ms); |
| } |
| |
| const PcmParameters& LeAudioGetSelectedHalPcmConfig() { |
| return transport_->LeAudioGetSelectedHalPcmConfig(); |
| } |
| |
| void LeAudioSetSelectedHalPcmConfig(SampleRate_2_1 sample_rate, |
| BitsPerSample bit_rate, |
| ChannelMode channel_mode, |
| uint32_t data_interval) { |
| transport_->LeAudioSetSelectedHalPcmConfig(sample_rate, bit_rate, |
| channel_mode, data_interval); |
| } |
| |
| bool SetAudioCtrlAckStateAndResetPendingStartStreamFlag( |
| BluetoothAudioCtrlAck state = BluetoothAudioCtrlAck::PENDING) { |
| return transport_->SetAudioCtrlAckStateAndResetPendingStartStreamFlag( |
| state); |
| } |
| |
| private: |
| LeAudioTransport* transport_; |
| }; |
| |
| // Instance of Le Audio to provide call-in APIs for Bluetooth Audio Hal |
| LeAudioSinkTransport* le_audio_sink = nullptr; |
| LeAudioSourceTransport* le_audio_source = nullptr; |
| } // namespace |
| |
| namespace bluetooth { |
| namespace audio { |
| namespace le_audio { |
| |
| LeAudioClientInterface* LeAudioClientInterface::interface = nullptr; |
| LeAudioClientInterface* LeAudioClientInterface::Get() { |
| if (osi_property_get_bool(BLUETOOTH_AUDIO_HAL_PROP_DISABLED, false)) { |
| LOG(ERROR) << __func__ << ": BluetoothAudio HAL is disabled"; |
| return nullptr; |
| } |
| |
| if (LeAudioClientInterface::interface == nullptr) |
| LeAudioClientInterface::interface = new LeAudioClientInterface(); |
| |
| return LeAudioClientInterface::interface; |
| } |
| |
| static SampleRate_2_1 le_audio_sample_rate2audio_hal(uint32_t sample_rate_2_1) { |
| switch (sample_rate_2_1) { |
| case 8000: |
| return SampleRate_2_1::RATE_8000; |
| case 16000: |
| return SampleRate_2_1::RATE_16000; |
| case 24000: |
| return SampleRate_2_1::RATE_24000; |
| case 32000: |
| return SampleRate_2_1::RATE_32000; |
| case 44100: |
| return SampleRate_2_1::RATE_44100; |
| case 48000: |
| return SampleRate_2_1::RATE_48000; |
| case 88200: |
| return SampleRate_2_1::RATE_88200; |
| case 96000: |
| return SampleRate_2_1::RATE_96000; |
| case 176400: |
| return SampleRate_2_1::RATE_176400; |
| case 192000: |
| return SampleRate_2_1::RATE_192000; |
| }; |
| return SampleRate_2_1::RATE_UNKNOWN; |
| } |
| |
| static BitsPerSample le_audio_bit_rate2audio_hal(uint8_t bits_per_sample) { |
| switch (bits_per_sample) { |
| case 16: |
| return BitsPerSample::BITS_16; |
| case 24: |
| return BitsPerSample::BITS_24; |
| case 32: |
| return BitsPerSample::BITS_32; |
| }; |
| return BitsPerSample::BITS_UNKNOWN; |
| } |
| |
| static ChannelMode le_audio_channel_mode2audio_hal(uint8_t channels_count) { |
| switch (channels_count) { |
| case 1: |
| return ChannelMode::MONO; |
| case 2: |
| return ChannelMode::STEREO; |
| } |
| return ChannelMode::UNKNOWN; |
| } |
| |
| void LeAudioClientInterface::Sink::Cleanup() { |
| LOG(INFO) << __func__ << " sink"; |
| StopSession(); |
| delete le_audio_sink_hal_clientinterface; |
| le_audio_sink_hal_clientinterface = nullptr; |
| delete le_audio_sink; |
| le_audio_sink = nullptr; |
| } |
| |
| void LeAudioClientInterface::Sink::SetPcmParameters( |
| const PcmParameters& params) { |
| le_audio_sink->LeAudioSetSelectedHalPcmConfig( |
| le_audio_sample_rate2audio_hal(params.sample_rate), |
| le_audio_bit_rate2audio_hal(params.bits_per_sample), |
| le_audio_channel_mode2audio_hal(params.channels_count), |
| params.data_interval_us); |
| } |
| |
| // Update Le Audio delay report to BluetoothAudio HAL |
| void LeAudioClientInterface::Sink::SetRemoteDelay(uint16_t delay_report_ms) { |
| LOG(INFO) << __func__ << ": delay_report_ms=" << delay_report_ms << " ms"; |
| le_audio_sink->SetRemoteDelay(delay_report_ms); |
| } |
| |
| void LeAudioClientInterface::Sink::StartSession() { |
| LOG(INFO) << __func__; |
| AudioConfiguration_2_1 audio_config; |
| audio_config.pcmConfig(le_audio_sink->LeAudioGetSelectedHalPcmConfig()); |
| if (!le_audio_sink_hal_clientinterface->UpdateAudioConfig_2_1(audio_config)) { |
| LOG(ERROR) << __func__ << ": cannot update audio config to HAL"; |
| return; |
| } |
| le_audio_sink_hal_clientinterface->StartSession_2_1(); |
| } |
| |
| void LeAudioClientInterface::Sink::ConfirmStreamingRequest() { |
| LOG(INFO) << __func__; |
| if (!le_audio_sink->SetAudioCtrlAckStateAndResetPendingStartStreamFlag()) { |
| LOG(WARNING) << ", no pending start stream request"; |
| return; |
| } |
| |
| le_audio_sink_hal_clientinterface->StreamStarted( |
| BluetoothAudioCtrlAck::SUCCESS_FINISHED); |
| } |
| |
| void LeAudioClientInterface::Sink::CancelStreamingRequest() { |
| LOG(INFO) << __func__; |
| if (!le_audio_sink->SetAudioCtrlAckStateAndResetPendingStartStreamFlag( |
| BluetoothAudioCtrlAck::FAILURE)) { |
| LOG(WARNING) << ", no pending start stream request"; |
| return; |
| } |
| |
| le_audio_sink_hal_clientinterface->StreamStarted( |
| BluetoothAudioCtrlAck::FAILURE); |
| } |
| |
| void LeAudioClientInterface::Sink::StopSession() { |
| LOG(INFO) << __func__ << " sink"; |
| le_audio_sink->SetAudioCtrlAckStateAndResetPendingStartStreamFlag(); |
| le_audio_sink_hal_clientinterface->EndSession(); |
| } |
| |
| size_t LeAudioClientInterface::Sink::Read(uint8_t* p_buf, uint32_t len) { |
| return le_audio_sink_hal_clientinterface->ReadAudioData(p_buf, len); |
| } |
| |
| void LeAudioClientInterface::Source::Cleanup() { |
| LOG(INFO) << __func__ << " source"; |
| StopSession(); |
| delete le_audio_source_hal_clientinterface; |
| le_audio_source_hal_clientinterface = nullptr; |
| delete le_audio_source; |
| le_audio_source = nullptr; |
| } |
| |
| void LeAudioClientInterface::Source::SetPcmParameters( |
| const PcmParameters& params) { |
| le_audio_source->LeAudioSetSelectedHalPcmConfig( |
| le_audio_sample_rate2audio_hal(params.sample_rate), |
| le_audio_bit_rate2audio_hal(params.bits_per_sample), |
| le_audio_channel_mode2audio_hal(params.channels_count), |
| params.data_interval_us); |
| } |
| |
| void LeAudioClientInterface::Source::SetRemoteDelay(uint16_t delay_report_ms) { |
| LOG(INFO) << __func__ << ": delay_report_ms=" << delay_report_ms << " ms"; |
| le_audio_source->SetRemoteDelay(delay_report_ms); |
| } |
| |
| void LeAudioClientInterface::Source::StartSession() { |
| LOG(INFO) << __func__; |
| if (!is_source_hal_enabled()) return; |
| AudioConfiguration_2_1 audio_config; |
| audio_config.pcmConfig(le_audio_source->LeAudioGetSelectedHalPcmConfig()); |
| if (!le_audio_source_hal_clientinterface->UpdateAudioConfig_2_1( |
| audio_config)) { |
| LOG(ERROR) << __func__ << ": cannot update audio config to HAL"; |
| return; |
| } |
| le_audio_source_hal_clientinterface->StartSession_2_1(); |
| } |
| |
| void LeAudioClientInterface::Source::ConfirmStreamingRequest() { |
| LOG(INFO) << __func__; |
| if (!le_audio_source->SetAudioCtrlAckStateAndResetPendingStartStreamFlag()) { |
| LOG(WARNING) << ", no pending start stream request"; |
| return; |
| } |
| |
| le_audio_source_hal_clientinterface->StreamStarted( |
| BluetoothAudioCtrlAck::SUCCESS_FINISHED); |
| } |
| |
| void LeAudioClientInterface::Source::CancelStreamingRequest() { |
| LOG(INFO) << __func__; |
| if (!le_audio_source->SetAudioCtrlAckStateAndResetPendingStartStreamFlag( |
| BluetoothAudioCtrlAck::FAILURE)) { |
| LOG(WARNING) << ", no pending start stream request"; |
| return; |
| } |
| |
| le_audio_source_hal_clientinterface->StreamStarted( |
| BluetoothAudioCtrlAck::FAILURE); |
| } |
| |
| void LeAudioClientInterface::Source::StopSession() { |
| LOG(INFO) << __func__ << " source"; |
| le_audio_source->SetAudioCtrlAckStateAndResetPendingStartStreamFlag(); |
| le_audio_source_hal_clientinterface->EndSession(); |
| } |
| |
| size_t LeAudioClientInterface::Source::Write(const uint8_t* p_buf, |
| uint32_t len) { |
| return le_audio_source_hal_clientinterface->WriteAudioData(p_buf, len); |
| } |
| |
| LeAudioClientInterface::Sink* LeAudioClientInterface::GetSink( |
| StreamCallbacks stream_cb, |
| bluetooth::common::MessageLoopThread* message_loop) { |
| if (sink_ == nullptr) { |
| sink_ = new Sink(); |
| } else { |
| LOG(WARNING) << __func__ << ", Sink is already acquired"; |
| return nullptr; |
| } |
| |
| LOG(INFO) << __func__; |
| |
| le_audio_sink = new LeAudioSinkTransport(std::move(stream_cb)); |
| le_audio_sink_hal_clientinterface = |
| new bluetooth::audio::BluetoothAudioSinkClientInterface(le_audio_sink, |
| message_loop); |
| if (!le_audio_sink_hal_clientinterface->IsValid()) { |
| LOG(WARNING) << __func__ |
| << ": BluetoothAudio HAL for Le Audio is invalid?!"; |
| delete le_audio_sink_hal_clientinterface; |
| le_audio_sink_hal_clientinterface = nullptr; |
| delete le_audio_sink; |
| le_audio_sink = nullptr; |
| delete sink_; |
| sink_ = nullptr; |
| |
| return nullptr; |
| } |
| |
| return sink_; |
| } |
| |
| bool LeAudioClientInterface::IsSinkAcquired() { return sink_ != nullptr; } |
| |
| bool LeAudioClientInterface::ReleaseSink(LeAudioClientInterface::Sink* sink) { |
| if (sink != sink_) { |
| LOG(WARNING) << __func__ << ", can't release not acquired sink"; |
| return false; |
| } |
| |
| if (le_audio_sink_hal_clientinterface && le_audio_sink) sink->Cleanup(); |
| |
| delete (sink_); |
| sink_ = nullptr; |
| |
| return true; |
| } |
| |
| LeAudioClientInterface::Source* LeAudioClientInterface::GetSource( |
| StreamCallbacks stream_cb, |
| bluetooth::common::MessageLoopThread* message_loop) { |
| if (source_ == nullptr) { |
| source_ = new Source(); |
| } else { |
| LOG(WARNING) << __func__ << ", Source is already acquired"; |
| return nullptr; |
| } |
| |
| LOG(INFO) << __func__; |
| |
| le_audio_source = new LeAudioSourceTransport(std::move(stream_cb)); |
| le_audio_source_hal_clientinterface = |
| new bluetooth::audio::BluetoothAudioSourceClientInterface(le_audio_source, |
| message_loop); |
| if (!le_audio_source_hal_clientinterface->IsValid()) { |
| LOG(WARNING) << __func__ |
| << ": BluetoothAudio HAL for Le Audio is invalid?!"; |
| delete le_audio_source_hal_clientinterface; |
| le_audio_source_hal_clientinterface = nullptr; |
| delete le_audio_source; |
| le_audio_source = nullptr; |
| delete source_; |
| source_ = nullptr; |
| |
| return nullptr; |
| } |
| |
| return source_; |
| } |
| |
| bool LeAudioClientInterface::IsSourceAcquired() { return source_ != nullptr; } |
| |
| bool LeAudioClientInterface::ReleaseSource( |
| LeAudioClientInterface::Source* source) { |
| if (source != source_) { |
| LOG(WARNING) << __func__ << ", can't release not acquired source"; |
| return false; |
| } |
| |
| if (le_audio_source_hal_clientinterface && le_audio_source) source->Cleanup(); |
| |
| delete (source_); |
| source_ = nullptr; |
| |
| return true; |
| } |
| |
| } // namespace le_audio |
| } // namespace audio |
| } // namespace bluetooth |