759 lines
27 KiB
C++
759 lines
27 KiB
C++
|
|
/*
|
||
|
|
* Copyright 2019 HIMSA II K/S - www.himsa.dk. 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 <hardware/bluetooth.h>
|
||
|
|
|
||
|
|
#include "client_interface.h"
|
||
|
|
#include "osi/include/log.h"
|
||
|
|
#include "osi/include/properties.h"
|
||
|
|
#include "btif/include/btif_hf.h"
|
||
|
|
|
||
|
|
bool gLeSinkState;
|
||
|
|
bool gLeSourceState;
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
/** M: Fix for build pass @{ */
|
||
|
|
using ::vendor::mediatek::hardware::bluetooth::audio::V2_1::LhdcLowLatencyEn;
|
||
|
|
/** @} */
|
||
|
|
using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::CodecType;
|
||
|
|
using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::LeAudioCodecConfiguration;
|
||
|
|
using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::PlcMethod;
|
||
|
|
using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::Lc3Parameters;
|
||
|
|
using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::Lc3FrameDuration;
|
||
|
|
using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::ConnParam;
|
||
|
|
using ::bluetooth::audio::AudioConfiguration;
|
||
|
|
using ::bluetooth::audio::AudioConfiguration_2_1;
|
||
|
|
using ::bluetooth::audio::BitsPerSample;
|
||
|
|
using ::bluetooth::audio::ChannelMode;
|
||
|
|
using ::bluetooth::audio::PcmParameters;
|
||
|
|
using ::bluetooth::audio::PcmParameters_2_1;
|
||
|
|
using ::bluetooth::audio::SampleRate;
|
||
|
|
using ::bluetooth::audio::SampleRate_2_1;
|
||
|
|
using ::bluetooth::audio::SessionType;
|
||
|
|
using ::bluetooth::audio::SessionType_2_1;
|
||
|
|
using ::bluetooth::audio::BluetoothAudioCtrlAck;
|
||
|
|
using ::bluetooth::audio::le_audio::StreamCallbacks;
|
||
|
|
using ::bluetooth::audio::BluetoothAudioSinkClientInterface;
|
||
|
|
using ::bluetooth::audio::BluetoothAudioSourceClientInterface;
|
||
|
|
|
||
|
|
#define AUDIO_STREAM_OUTPUT_BUFFER_SZ (28 * 512)
|
||
|
|
|
||
|
|
#define LE_AUDIO_OFFLOAD_PROP "persist.vendor.bluetooth.leaudio_mode"
|
||
|
|
#define LE_AUDIO_OFFLOAD_UMS 0x01
|
||
|
|
#define LE_AUDIO_OFFLOAD_CG 0x02
|
||
|
|
#define LE_AUDIO_OFFLOAD_MODE_UNINIT 0xF0
|
||
|
|
unsigned int g_leaudio_offload_mode = LE_AUDIO_OFFLOAD_MODE_UNINIT;
|
||
|
|
// Sink transport implementation for Le Audio
|
||
|
|
class LeAudioSinkTransport
|
||
|
|
: public bluetooth::audio::IBluetoothSinkTransportInstance {
|
||
|
|
public:
|
||
|
|
LeAudioSinkTransport(SessionType_2_1 sessionType, StreamCallbacks stream_cb)
|
||
|
|
: IBluetoothSinkTransportInstance(
|
||
|
|
sessionType, {}),
|
||
|
|
stream_cb_(std::move(stream_cb)),
|
||
|
|
remote_delay_report_ms_(0),
|
||
|
|
total_bytes_read_(0),
|
||
|
|
data_position_({}),
|
||
|
|
/*pcm_config_({SampleRate::RATE_16000, ChannelMode::STEREO,
|
||
|
|
BitsPerSample::BITS_16}){};*/
|
||
|
|
/** M: Fix for build pass @{ */
|
||
|
|
pcm_config_({SampleRate_2_1::RATE_16000, ChannelMode::STEREO,
|
||
|
|
BitsPerSample::BITS_16, LhdcLowLatencyEn::Disabled,0}){};
|
||
|
|
/** @} */
|
||
|
|
|
||
|
|
BluetoothAudioCtrlAck StartRequest() override {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
|
||
|
|
// Don't send START request to stack while we are in a call
|
||
|
|
if (!bluetooth::headset::IsCallIdleForCG()) {
|
||
|
|
LOG(ERROR) << __func__ << ": call state is busy";
|
||
|
|
return BluetoothAudioCtrlAck::FAILURE;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (stream_cb_.on_resume_(true)) {
|
||
|
|
return BluetoothAudioCtrlAck::SUCCESS_FINISHED;
|
||
|
|
}
|
||
|
|
return BluetoothAudioCtrlAck::FAILURE;
|
||
|
|
}
|
||
|
|
|
||
|
|
BluetoothAudioCtrlAck SuspendRequest() override {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (stream_cb_.on_suspend_()) {
|
||
|
|
if (!::bluetooth::audio::le_audio::is_leaudio_offload_enabled()) {
|
||
|
|
uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2];
|
||
|
|
::bluetooth::audio::le_audio::read(p_buf, sizeof(p_buf));
|
||
|
|
}
|
||
|
|
return BluetoothAudioCtrlAck::SUCCESS_FINISHED;
|
||
|
|
} else {
|
||
|
|
return BluetoothAudioCtrlAck::FAILURE;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void StopRequest() override {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (stream_cb_.on_suspend_()) {
|
||
|
|
// flush
|
||
|
|
uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2];
|
||
|
|
::bluetooth::audio::le_audio::read(p_buf, sizeof(p_buf));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
|
||
|
|
uint64_t* total_bytes_read,
|
||
|
|
timespec* data_position) override {
|
||
|
|
/*VLOG(2) << __func__ << ": data=" << total_bytes_read_
|
||
|
|
<< " 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_read != nullptr) *total_bytes_read = total_bytes_read_;
|
||
|
|
if (data_position != nullptr) *data_position = data_position_;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MetadataChanged(const source_metadata_t& source_metadata) override {
|
||
|
|
auto track_count = source_metadata.track_count;
|
||
|
|
auto tracks = source_metadata.tracks;
|
||
|
|
LOG(INFO) << __func__ << ": " << track_count << " track(s) received";
|
||
|
|
while (track_count) {
|
||
|
|
VLOG(1) << __func__ << ": usage=" << tracks->usage
|
||
|
|
<< ", content_type=" << tracks->content_type
|
||
|
|
<< ", gain=" << tracks->gain;
|
||
|
|
--track_count;
|
||
|
|
++tracks;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void EnterGameMode(int enter) override {
|
||
|
|
VLOG(1) << __func__ << ": ble audio Sink enter = " << enter;
|
||
|
|
stream_cb_.on_gamemode_(enter);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ResetPresentationPosition() override {
|
||
|
|
VLOG(2) << __func__ << ": called.";
|
||
|
|
remote_delay_report_ms_ = 0;
|
||
|
|
total_bytes_read_ = 0;
|
||
|
|
data_position_ = {};
|
||
|
|
}
|
||
|
|
|
||
|
|
void LogBytesRead(size_t bytes_read) override {
|
||
|
|
if (bytes_read) {
|
||
|
|
total_bytes_read_ += bytes_read;
|
||
|
|
clock_gettime(CLOCK_MONOTONIC, &data_position_);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool* GetBluetoothStateAddress() {
|
||
|
|
return &gLeSinkState;
|
||
|
|
}
|
||
|
|
|
||
|
|
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_2_1& LeAudioGetSelectedHalPcmConfig() { return pcm_config_; }
|
||
|
|
|
||
|
|
void LeAudioSetSelectedHalPcmConfig(SampleRate_2_1 sample_rate,
|
||
|
|
BitsPerSample bit_rate,
|
||
|
|
ChannelMode channel_mode,
|
||
|
|
int gamemode,
|
||
|
|
int data_interval_ms) {
|
||
|
|
pcm_config_.sampleRate = sample_rate;
|
||
|
|
pcm_config_.bitsPerSample = bit_rate;
|
||
|
|
pcm_config_.channelMode = channel_mode;
|
||
|
|
pcm_config_.isLowLatencyEnabled = (gamemode == 1) ?
|
||
|
|
LhdcLowLatencyEn::Enabled : LhdcLowLatencyEn::Disabled;
|
||
|
|
pcm_config_.dataIntervalUs = data_interval_ms*1000;
|
||
|
|
}
|
||
|
|
|
||
|
|
const LeAudioCodecConfiguration& LeAudioGetSelectedHalCodecConfig() {
|
||
|
|
return leAudioCodecConfig_;
|
||
|
|
}
|
||
|
|
|
||
|
|
void LeAudioSetSelectedHalCodecConfig(CodecType codec_type, uint32_t audio_channel_allocation,
|
||
|
|
uint32_t encoded_audio_bitrate, uint8_t le_audio_type,
|
||
|
|
Lc3Parameters lc3_config) {
|
||
|
|
leAudioCodecConfig_.codecType = codec_type;
|
||
|
|
leAudioCodecConfig_.audioChannelAllocation = audio_channel_allocation;
|
||
|
|
leAudioCodecConfig_.encodedAudioBitrate = encoded_audio_bitrate;
|
||
|
|
leAudioCodecConfig_.le_audio_type = le_audio_type;
|
||
|
|
leAudioCodecConfig_.bfi_ext = false;
|
||
|
|
leAudioCodecConfig_.plc_method = PlcMethod::STANDARD_PLC;
|
||
|
|
leAudioCodecConfig_.lc3Config = lc3_config;
|
||
|
|
LOG(INFO) << __func__ << ": LeAudioCodecConfiguration=" << toString(leAudioCodecConfig_);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const ConnParam& LeAudioSetConnParameter(uint16_t l_cis_conn_hdl, uint16_t r_cis_conn_hdl, uint8_t bn) {
|
||
|
|
connParam_.conn_handle_R = l_cis_conn_hdl;
|
||
|
|
connParam_.conn_handle_L = r_cis_conn_hdl;
|
||
|
|
connParam_.bn = bn;
|
||
|
|
LOG(INFO) << __func__ << ": Connection Parameter=" << toString(connParam_);
|
||
|
|
return connParam_;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
StreamCallbacks stream_cb_;
|
||
|
|
uint16_t remote_delay_report_ms_;
|
||
|
|
uint64_t total_bytes_read_;
|
||
|
|
timespec data_position_;
|
||
|
|
PcmParameters_2_1 pcm_config_;
|
||
|
|
LeAudioCodecConfiguration leAudioCodecConfig_;
|
||
|
|
ConnParam connParam_;
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
class LeAudioSourceTransport
|
||
|
|
: public bluetooth::audio::IBluetoothSourceTransportInstance {
|
||
|
|
public:
|
||
|
|
LeAudioSourceTransport(SessionType_2_1 sessionType, StreamCallbacks stream_cb)
|
||
|
|
: IBluetoothSourceTransportInstance(
|
||
|
|
sessionType, {}),
|
||
|
|
stream_cb_(std::move(stream_cb)),
|
||
|
|
remote_delay_report_ms_(0),
|
||
|
|
total_bytes_written_(0),
|
||
|
|
data_position_({}),
|
||
|
|
/*pcm_config_({SampleRate::RATE_16000, ChannelMode::MONO,
|
||
|
|
BitsPerSample::BITS_16}){};*/
|
||
|
|
/** M: Fix for build pass @{ */
|
||
|
|
pcm_config_({SampleRate_2_1::RATE_16000, ChannelMode::MONO,
|
||
|
|
BitsPerSample::BITS_16, LhdcLowLatencyEn::Disabled, 0}){};
|
||
|
|
/** @} */
|
||
|
|
|
||
|
|
BluetoothAudioCtrlAck StartRequest() override {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (stream_cb_.on_resume_(true)) {
|
||
|
|
return BluetoothAudioCtrlAck::SUCCESS_FINISHED;
|
||
|
|
}
|
||
|
|
return BluetoothAudioCtrlAck::FAILURE;
|
||
|
|
}
|
||
|
|
|
||
|
|
BluetoothAudioCtrlAck SuspendRequest() override {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (stream_cb_.on_suspend_()) {
|
||
|
|
if (!::bluetooth::audio::le_audio::is_leaudio_offload_enabled()) {
|
||
|
|
/* TODO (gkolodziejczyk): Clean exact value of buffer */
|
||
|
|
uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2];
|
||
|
|
::bluetooth::audio::le_audio::read(p_buf, sizeof(p_buf));
|
||
|
|
}
|
||
|
|
return BluetoothAudioCtrlAck::SUCCESS_FINISHED;
|
||
|
|
} else {
|
||
|
|
return BluetoothAudioCtrlAck::FAILURE;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void StopRequest() override {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (stream_cb_.on_suspend_()) {
|
||
|
|
if (!::bluetooth::audio::le_audio::is_leaudio_offload_enabled()) {
|
||
|
|
// flush
|
||
|
|
/* TODO (gkolodziejczyk): Clean exact value of buffer */
|
||
|
|
uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2];
|
||
|
|
::bluetooth::audio::le_audio::read(p_buf, sizeof(p_buf));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* TODO (gkolodziejczyk): Shouldn't this be in oposite ? Set for AF */
|
||
|
|
bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
|
||
|
|
uint64_t* total_bytes_written,
|
||
|
|
timespec* data_position) override {
|
||
|
|
VLOG(2) << __func__ << ": data=" << total_bytes_written_
|
||
|
|
<< " 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_written != nullptr)
|
||
|
|
*total_bytes_written = total_bytes_written_;
|
||
|
|
if (data_position != nullptr) *data_position = data_position_;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MetadataChanged(const source_metadata_t& source_metadata) override {
|
||
|
|
auto track_count = source_metadata.track_count;
|
||
|
|
auto tracks = source_metadata.tracks;
|
||
|
|
LOG(INFO) << __func__ << ": " << track_count << " track(s) received";
|
||
|
|
while (track_count) {
|
||
|
|
VLOG(1) << __func__ << ": usage=" << tracks->usage
|
||
|
|
<< ", content_type=" << tracks->content_type
|
||
|
|
<< ", gain=" << tracks->gain;
|
||
|
|
--track_count;
|
||
|
|
++tracks;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void EnterGameMode(int enter) override {
|
||
|
|
VLOG(1) << __func__ << ": ble audio Source enter = " << enter;
|
||
|
|
stream_cb_.on_gamemode_(enter);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ResetPresentationPosition() override {
|
||
|
|
VLOG(2) << __func__ << ": called.";
|
||
|
|
remote_delay_report_ms_ = 0;
|
||
|
|
total_bytes_written_ = 0;
|
||
|
|
data_position_ = {};
|
||
|
|
}
|
||
|
|
|
||
|
|
void LogBytesWritten(size_t bytes_written) override {
|
||
|
|
if (bytes_written) {
|
||
|
|
total_bytes_written_ += bytes_written;
|
||
|
|
clock_gettime(CLOCK_MONOTONIC, &data_position_);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void LogBytesRead(size_t bytes_read) override {
|
||
|
|
}
|
||
|
|
|
||
|
|
bool* GetBluetoothStateAddress() {
|
||
|
|
return &gLeSourceState;
|
||
|
|
}
|
||
|
|
|
||
|
|
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_2_1& LeAudioGetSelectedHalPcmConfig() { return pcm_config_; }
|
||
|
|
|
||
|
|
void LeAudioSetSelectedHalPcmConfig(SampleRate_2_1 sample_rate,
|
||
|
|
BitsPerSample bit_rate,
|
||
|
|
ChannelMode channel_mode,
|
||
|
|
int data_interval_ms) {
|
||
|
|
pcm_config_.sampleRate = sample_rate;
|
||
|
|
pcm_config_.bitsPerSample = bit_rate;
|
||
|
|
pcm_config_.channelMode = channel_mode;
|
||
|
|
pcm_config_.dataIntervalUs = data_interval_ms*1000;
|
||
|
|
}
|
||
|
|
|
||
|
|
const LeAudioCodecConfiguration& LeAudioGetSelectedHalCodecConfig() {
|
||
|
|
return leAudioCodecConfig_;
|
||
|
|
}
|
||
|
|
|
||
|
|
void LeAudioSetSelectedHalCodecConfig(CodecType codec_type, uint32_t audio_channel_allocation,
|
||
|
|
uint32_t encoded_audio_bitrate, uint8_t le_audio_type,
|
||
|
|
Lc3Parameters lc3_config) {
|
||
|
|
leAudioCodecConfig_.codecType = codec_type;
|
||
|
|
leAudioCodecConfig_.audioChannelAllocation = audio_channel_allocation;
|
||
|
|
leAudioCodecConfig_.encodedAudioBitrate = encoded_audio_bitrate;
|
||
|
|
leAudioCodecConfig_.le_audio_type = le_audio_type;
|
||
|
|
leAudioCodecConfig_.bfi_ext = false;
|
||
|
|
leAudioCodecConfig_.plc_method = PlcMethod::STANDARD_PLC;
|
||
|
|
leAudioCodecConfig_.lc3Config = lc3_config;
|
||
|
|
LOG(INFO) << __func__ << ": LeAudioCodecConfiguration=" << toString(leAudioCodecConfig_);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const ConnParam& LeAudioSetConnParameter(uint16_t l_cis_conn_hdl, uint16_t r_cis_conn_hdl, uint8_t bn) {
|
||
|
|
connParam_.conn_handle_R = l_cis_conn_hdl;
|
||
|
|
connParam_.conn_handle_L = r_cis_conn_hdl;
|
||
|
|
connParam_.bn = bn;
|
||
|
|
LOG(INFO) << __func__ << ": Connection Parameter=" << toString(connParam_);
|
||
|
|
return connParam_;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
StreamCallbacks stream_cb_;
|
||
|
|
uint16_t remote_delay_report_ms_;
|
||
|
|
uint64_t total_bytes_written_;
|
||
|
|
timespec data_position_;
|
||
|
|
PcmParameters_2_1 pcm_config_;
|
||
|
|
LeAudioCodecConfiguration leAudioCodecConfig_;
|
||
|
|
ConnParam connParam_;
|
||
|
|
};
|
||
|
|
|
||
|
|
// Instance of Le Audio to provide call-in APIs for Bluetooth Audio Hal
|
||
|
|
LeAudioSinkTransport* le_audio_sink = nullptr;
|
||
|
|
LeAudioSourceTransport* le_audio_source = nullptr;
|
||
|
|
// Common interfaces to call-out into Bluetooth Audio Hal
|
||
|
|
bluetooth::audio::BluetoothAudioSinkClientInterface*
|
||
|
|
le_audio_sink_hal_clientinterface = nullptr;
|
||
|
|
bluetooth::audio::BluetoothAudioSourceClientInterface*
|
||
|
|
le_audio_source_hal_clientinterface = nullptr;
|
||
|
|
bool btaudio_le_audio_disabled = false;
|
||
|
|
bool is_configured = false;
|
||
|
|
|
||
|
|
// Save the value if the remote reports its delay before le_audio_sink is
|
||
|
|
// initialized
|
||
|
|
uint16_t remote_delay_ms = 0;
|
||
|
|
|
||
|
|
bool is_hal_2_0_force_disabled() {
|
||
|
|
if (!is_configured) {
|
||
|
|
btaudio_le_audio_disabled =
|
||
|
|
osi_property_get_bool(BLUETOOTH_AUDIO_HAL_PROP_DISABLED, false);
|
||
|
|
is_configured = true;
|
||
|
|
}
|
||
|
|
return btaudio_le_audio_disabled;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
namespace bluetooth {
|
||
|
|
namespace audio {
|
||
|
|
namespace le_audio {
|
||
|
|
|
||
|
|
/* TODO (gkolodziejczyk): Do it smarter. btw. do this really check if hal 2.0 is
|
||
|
|
* enabled ?
|
||
|
|
*/
|
||
|
|
bool is_sink_hal_enabled() {
|
||
|
|
return le_audio_sink_hal_clientinterface != nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool is_source_hal_enabled() {
|
||
|
|
return le_audio_source_hal_clientinterface != nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
//TODO
|
||
|
|
bool is_leaudio_offload_enabled() {
|
||
|
|
if (g_leaudio_offload_mode == LE_AUDIO_OFFLOAD_MODE_UNINIT) {
|
||
|
|
char value[PROPERTY_VALUE_MAX];
|
||
|
|
value[0] = '\0';
|
||
|
|
osi_property_get(LE_AUDIO_OFFLOAD_PROP, value, "off");
|
||
|
|
g_leaudio_offload_mode = 0;
|
||
|
|
if (strcmp(value,"ums-cg") == 0) {
|
||
|
|
g_leaudio_offload_mode |= LE_AUDIO_OFFLOAD_UMS;
|
||
|
|
g_leaudio_offload_mode |= LE_AUDIO_OFFLOAD_CG;
|
||
|
|
}
|
||
|
|
LOG(INFO) << __func__ << ", le offload prop value: " << value <<" mode: " << g_leaudio_offload_mode;
|
||
|
|
}
|
||
|
|
return (g_leaudio_offload_mode & (LE_AUDIO_OFFLOAD_UMS | LE_AUDIO_OFFLOAD_CG)) ? true:false;
|
||
|
|
}
|
||
|
|
/* TODO (gkolodziejczyk): Think about optimization sink and source methods */
|
||
|
|
bool init_sink(StreamCallbacks stream_cb,
|
||
|
|
bluetooth::common::MessageLoopThread* message_loop) {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
|
||
|
|
if (is_hal_2_0_force_disabled()) {
|
||
|
|
LOG(ERROR) << __func__ << ": BluetoothAudio HAL is disabled";
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (is_leaudio_offload_enabled()) {
|
||
|
|
le_audio_sink =
|
||
|
|
new LeAudioSinkTransport(
|
||
|
|
SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH,std::move(stream_cb));
|
||
|
|
} else {
|
||
|
|
le_audio_sink =
|
||
|
|
new LeAudioSinkTransport(
|
||
|
|
SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH, std::move(stream_cb));
|
||
|
|
}
|
||
|
|
le_audio_sink_hal_clientinterface =
|
||
|
|
new 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;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (remote_delay_ms != 0) {
|
||
|
|
LOG(INFO) << __func__ << ": restore DELAY " << remote_delay_ms << " ms";
|
||
|
|
le_audio_sink->SetRemoteDelay(remote_delay_ms);
|
||
|
|
remote_delay_ms = 0;
|
||
|
|
}
|
||
|
|
gLeSinkState = true;
|
||
|
|
LOG(INFO) << __func__ << ": gLeSinkState = " << (bool*)&gLeSinkState;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool init_source(StreamCallbacks stream_cb,
|
||
|
|
bluetooth::common::MessageLoopThread* message_loop) {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
|
||
|
|
if (is_hal_2_0_force_disabled()) {
|
||
|
|
LOG(ERROR) << __func__ << ": BluetoothAudio HAL is disabled";
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (is_leaudio_offload_enabled()) {
|
||
|
|
le_audio_source =
|
||
|
|
new LeAudioSourceTransport(
|
||
|
|
SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH,std::move(stream_cb));
|
||
|
|
} else {
|
||
|
|
le_audio_source =
|
||
|
|
new LeAudioSourceTransport(
|
||
|
|
SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH, std::move(stream_cb));
|
||
|
|
}
|
||
|
|
le_audio_source_hal_clientinterface =
|
||
|
|
new bluetooth::audio::BluetoothAudioSourceClientInterface(le_audio_source,
|
||
|
|
message_loop);
|
||
|
|
if (!le_audio_sink_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;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (remote_delay_ms != 0) {
|
||
|
|
LOG(INFO) << __func__ << ": restore DELAY " << remote_delay_ms << " ms";
|
||
|
|
le_audio_source->SetRemoteDelay(remote_delay_ms);
|
||
|
|
remote_delay_ms = 0;
|
||
|
|
}
|
||
|
|
gLeSourceState = true;
|
||
|
|
LOG(INFO) << __func__ << ": gLeSourceState = " << (bool*)&gLeSourceState;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void cleanup_sink() {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
gLeSinkState = false;
|
||
|
|
if (!is_sink_hal_enabled()) return;
|
||
|
|
end_sink_session();
|
||
|
|
delete le_audio_sink_hal_clientinterface;
|
||
|
|
le_audio_sink_hal_clientinterface = nullptr;
|
||
|
|
delete le_audio_sink;
|
||
|
|
le_audio_sink = nullptr;
|
||
|
|
remote_delay_ms = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void cleanup_source() {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
gLeSourceState = false;
|
||
|
|
if (!is_source_hal_enabled()) return;
|
||
|
|
end_source_session();
|
||
|
|
delete le_audio_source_hal_clientinterface;
|
||
|
|
le_audio_source_hal_clientinterface = nullptr;
|
||
|
|
delete le_audio_source;
|
||
|
|
le_audio_source = nullptr;
|
||
|
|
remote_delay_ms = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void start_sink_session() {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (!is_sink_hal_enabled()) return;
|
||
|
|
AudioConfiguration_2_1 audio_config;
|
||
|
|
if (is_leaudio_offload_enabled()) {
|
||
|
|
audio_config.leAudioCodecConfig(le_audio_sink->LeAudioGetSelectedHalCodecConfig());
|
||
|
|
} else {
|
||
|
|
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 start_source_session() {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (!is_source_hal_enabled()) return;
|
||
|
|
AudioConfiguration_2_1 audio_config;
|
||
|
|
if (is_leaudio_offload_enabled()) {
|
||
|
|
audio_config.leAudioCodecConfig(le_audio_source->LeAudioGetSelectedHalCodecConfig());
|
||
|
|
} else {
|
||
|
|
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 update_source_conn_param(uint16_t l_cis_conn_hdl, uint16_t r_cis_conn_hdl, uint8_t bn) {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (!is_source_hal_enabled()) return;
|
||
|
|
AudioConfiguration_2_1 audio_config;
|
||
|
|
if (is_leaudio_offload_enabled()) {
|
||
|
|
if (!le_audio_source_hal_clientinterface->UpdateConnParam(
|
||
|
|
le_audio_source->LeAudioSetConnParameter(l_cis_conn_hdl, r_cis_conn_hdl, bn))) {
|
||
|
|
LOG(ERROR) << __func__ << ": cannot update connection parameter to HAL";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void update_sink_conn_param(uint16_t l_cis_conn_hdl, uint16_t r_cis_conn_hdl, uint8_t bn) {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (!is_source_hal_enabled()) return;
|
||
|
|
if (is_leaudio_offload_enabled()) {
|
||
|
|
if (!le_audio_sink_hal_clientinterface->UpdateConnParam(
|
||
|
|
le_audio_sink->LeAudioSetConnParameter(l_cis_conn_hdl, r_cis_conn_hdl, bn))) {
|
||
|
|
LOG(ERROR) << __func__ << ": cannot update connection parameter to HAL";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void end_sink_session() {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (!is_sink_hal_enabled()) return;
|
||
|
|
le_audio_sink_hal_clientinterface->EndSession();
|
||
|
|
}
|
||
|
|
|
||
|
|
void end_source_session() {
|
||
|
|
LOG(INFO) << __func__;
|
||
|
|
if (!is_source_hal_enabled()) return;
|
||
|
|
le_audio_source_hal_clientinterface->EndSession();
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t read(uint8_t* p_buf, uint32_t len) {
|
||
|
|
if (!is_sink_hal_enabled()) return 0;
|
||
|
|
return le_audio_sink_hal_clientinterface->ReadAudioData(p_buf, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t write(const uint8_t* p_buf, uint32_t len) {
|
||
|
|
if (!is_source_hal_enabled()) return 0;
|
||
|
|
return le_audio_source_hal_clientinterface->WriteAudioData(p_buf, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update Le Audio delay report to BluetoothAudio HAL
|
||
|
|
void set_sink_remote_delay(uint16_t delay_report_ms) {
|
||
|
|
if (!is_sink_hal_enabled()) {
|
||
|
|
LOG(INFO) << __func__ << ": not ready for DelayReport " << delay_report_ms
|
||
|
|
<< " ms";
|
||
|
|
remote_delay_ms = delay_report_ms;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
LOG(INFO) << __func__ << ": delay_report_ms=" << delay_report_ms << " ms";
|
||
|
|
le_audio_sink->SetRemoteDelay(delay_report_ms);
|
||
|
|
}
|
||
|
|
|
||
|
|
void set_source_remote_delay(uint16_t delay_report_ms) {
|
||
|
|
if (!is_source_hal_enabled()) {
|
||
|
|
LOG(INFO) << __func__ << ": not ready for DelayReport " << delay_report_ms
|
||
|
|
<< " ms";
|
||
|
|
remote_delay_ms = delay_report_ms;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
LOG(INFO) << __func__ << ": delay_report_ms=" << delay_report_ms << " ms";
|
||
|
|
le_audio_source->SetRemoteDelay(delay_report_ms);
|
||
|
|
}
|
||
|
|
|
||
|
|
static SampleRate_2_1 le_audio_sample_rate2audio_hal(uint32_t sample_rate) {
|
||
|
|
switch (sample_rate) {
|
||
|
|
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;
|
||
|
|
case 24000:
|
||
|
|
return SampleRate_2_1::RATE_24000;
|
||
|
|
case 16000:
|
||
|
|
return SampleRate_2_1::RATE_16000;
|
||
|
|
case 32000:
|
||
|
|
return SampleRate_2_1::RATE_32000;
|
||
|
|
case 8000:
|
||
|
|
return SampleRate_2_1::RATE_8000;
|
||
|
|
};
|
||
|
|
return SampleRate_2_1::RATE_UNKNOWN;
|
||
|
|
}
|
||
|
|
|
||
|
|
static BitsPerSample le_audio_bit_rate2audio_hal(uint8_t bitrate) {
|
||
|
|
switch (bitrate) {
|
||
|
|
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 channel_mode) {
|
||
|
|
switch (channel_mode) {
|
||
|
|
case 1:
|
||
|
|
return ChannelMode::MONO;
|
||
|
|
case 2:
|
||
|
|
return ChannelMode::STEREO;
|
||
|
|
}
|
||
|
|
return ChannelMode::UNKNOWN;
|
||
|
|
}
|
||
|
|
|
||
|
|
static Lc3FrameDuration le_audio_duration_mode2audio_hal(uint16_t frame_duration) {
|
||
|
|
switch (frame_duration) {
|
||
|
|
case 10:
|
||
|
|
return Lc3FrameDuration::DURATION_10000US;
|
||
|
|
}
|
||
|
|
return Lc3FrameDuration::DURATION_7500US;
|
||
|
|
}
|
||
|
|
|
||
|
|
static CodecType le_audio_duration_codec_hal(uint8_t codec_type) {
|
||
|
|
return CodecType::LC3;
|
||
|
|
}
|
||
|
|
|
||
|
|
void set_sink_pcm_parameters(uint32_t sample_rate, uint8_t bit_rate,
|
||
|
|
uint8_t channel_mode, int gamemode, int data_interval_ms) {
|
||
|
|
le_audio_sink->LeAudioSetSelectedHalPcmConfig(
|
||
|
|
le_audio_sample_rate2audio_hal(sample_rate),
|
||
|
|
le_audio_bit_rate2audio_hal(bit_rate),
|
||
|
|
le_audio_channel_mode2audio_hal(channel_mode),
|
||
|
|
gamemode,data_interval_ms);
|
||
|
|
}
|
||
|
|
|
||
|
|
void set_sink_offload_parameters(uint8_t codec_type, uint8_t audio_channel_allocation,
|
||
|
|
uint32_t encoded_audio_bitrate, uint8_t le_audio_type, uint32_t bits_per_sample,
|
||
|
|
uint32_t sample_rate, uint16_t data_interval_ms, uint32_t sdu_size) {
|
||
|
|
Lc3Parameters lc3_config;
|
||
|
|
lc3_config.pcmBitDepth = le_audio_bit_rate2audio_hal(bits_per_sample);
|
||
|
|
lc3_config.samplingFrequency = le_audio_sample_rate2audio_hal(sample_rate);
|
||
|
|
lc3_config.frameDuration = le_audio_duration_mode2audio_hal(data_interval_ms);
|
||
|
|
lc3_config.octetsPerFrame = sdu_size;
|
||
|
|
lc3_config.blocksPerSdu = 0;
|
||
|
|
le_audio_sink->LeAudioSetSelectedHalCodecConfig(le_audio_duration_codec_hal(codec_type),
|
||
|
|
(uint32_t)audio_channel_allocation, encoded_audio_bitrate, le_audio_type, lc3_config);
|
||
|
|
}
|
||
|
|
|
||
|
|
void set_source_pcm_parameters(uint32_t sample_rate, uint8_t bit_rate,
|
||
|
|
uint8_t channel_mode, int data_interval_ms) {
|
||
|
|
le_audio_source->LeAudioSetSelectedHalPcmConfig(
|
||
|
|
le_audio_sample_rate2audio_hal(sample_rate),
|
||
|
|
le_audio_bit_rate2audio_hal(bit_rate),
|
||
|
|
le_audio_channel_mode2audio_hal(channel_mode),
|
||
|
|
data_interval_ms);
|
||
|
|
}
|
||
|
|
|
||
|
|
void set_source_offload_parameters(uint8_t codec_type, uint8_t audio_channel_allocation,
|
||
|
|
uint32_t encoded_audio_bitrate, uint8_t le_audio_type, uint32_t bits_per_sample,
|
||
|
|
uint32_t sample_rate, uint16_t data_interval_ms, uint32_t sdu_size) {
|
||
|
|
Lc3Parameters lc3_config;
|
||
|
|
lc3_config.pcmBitDepth = le_audio_bit_rate2audio_hal(bits_per_sample);
|
||
|
|
lc3_config.samplingFrequency = le_audio_sample_rate2audio_hal(sample_rate);
|
||
|
|
lc3_config.frameDuration = le_audio_duration_mode2audio_hal(data_interval_ms);
|
||
|
|
lc3_config.octetsPerFrame = sdu_size;
|
||
|
|
lc3_config.blocksPerSdu = 0;
|
||
|
|
le_audio_source->LeAudioSetSelectedHalCodecConfig(le_audio_duration_codec_hal(codec_type),
|
||
|
|
(uint32_t)audio_channel_allocation, encoded_audio_bitrate, le_audio_type, lc3_config);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace le_audio
|
||
|
|
} // namespace audio
|
||
|
|
} // namespace bluetooth
|