summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2022-01-20 01:20:15 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2022-01-20 01:20:15 +0000
commit0ade88c6039fb491f54d95e50edb45984be2c883 (patch)
treebe38f66ff2676e18c27d70f9336cc5de19a364cd
parente08c6a59957172be671dc3b499e601e38ce64c17 (diff)
parent9df0fd80f092e9cfd7303fb0f9708f024d6f39c1 (diff)
Merge "leaudio: Use fixed audio session configuration while connected"
-rw-r--r--system/bta/le_audio/client.cc391
-rw-r--r--system/bta/le_audio/devices.cc2
-rw-r--r--system/bta/le_audio/le_audio_client_test.cc79
-rw-r--r--system/bta/le_audio/le_audio_types.cc5
-rw-r--r--system/bta/le_audio/le_audio_types.h3
5 files changed, 249 insertions, 231 deletions
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index fd72d5b4d8..3aa605e581 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -167,7 +167,6 @@ class LeAudioClientImpl : public LeAudioClient {
callbacks_(callbacks_),
active_group_id_(bluetooth::groups::kGroupUnknown),
current_context_type_(LeAudioContextType::MEDIA),
- upcoming_context_type_(LeAudioContextType::MEDIA),
stream_setup_start_timestamp_(0),
stream_setup_end_timestamp_(0),
audio_receiver_state_(AudioState::IDLE),
@@ -530,6 +529,7 @@ class LeAudioClientImpl : public LeAudioClient {
LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
auto final_context_type = context_type;
+ DLOG(INFO) << __func__;
if (context_type >= static_cast<uint16_t>(LeAudioContextType::RFU)) {
LOG(ERROR) << __func__ << ", stream context type is not supported: "
<< loghex(context_type);
@@ -654,6 +654,8 @@ class LeAudioClientImpl : public LeAudioClient {
if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);
StopAudio();
+ ClientAudioIntefraceRelease();
+
GroupStop(active_group_id_);
callbacks_->OnGroupStatus(active_group_id_, GroupStatus::INACTIVE);
active_group_id_ = group_id;
@@ -691,8 +693,11 @@ class LeAudioClientImpl : public LeAudioClient {
}
}
- /* Configure audio HAL sessions with most frequent context */
- UpdateCurrentHalSessions(group_id, current_context_type_);
+ /* Configure audio HAL sessions with most frequent context.
+ * If reconfiguration is not needed it means, context type is not supported
+ */
+ UpdateConfigAndCheckIfReconfigurationIsNeeded(group_id,
+ LeAudioContextType::MEDIA);
if (current_source_codec_config.IsInvalid() &&
current_sink_codec_config.IsInvalid()) {
LOG(WARNING) << __func__ << ", unsupported device configurations";
@@ -700,6 +705,18 @@ class LeAudioClientImpl : public LeAudioClient {
return;
}
+ /* Expose audio sessions */
+ audio_framework_source_config.data_interval_us =
+ current_source_codec_config.data_interval_us;
+ LeAudioClientAudioSource::Start(audio_framework_source_config,
+ audioSinkReceiver);
+
+ audio_framework_sink_config.data_interval_us =
+ current_source_codec_config.data_interval_us;
+
+ LeAudioClientAudioSink::Start(audio_framework_sink_config,
+ audioSourceReceiver);
+
active_group_id_ = group_id;
callbacks_->OnGroupStatus(active_group_id_, GroupStatus::ACTIVE);
}
@@ -1710,12 +1727,21 @@ class LeAudioClientImpl : public LeAudioClient {
if (audio_sender_state_ == AudioState::IDLE &&
audio_receiver_state_ == AudioState::IDLE) {
DLOG(INFO) << __func__
- << " Device not streaming but active. Lets update audio "
- "session to match needed channel number";
- UpdateCurrentHalSessions(active_group_id_, current_context_type_);
+ << " Device not streaming but active - nothing to do";
+ return;
+ }
+
+ auto num_of_devices =
+ get_num_of_devices_in_configuration(stream_conf->conf);
+
+ if (num_of_devices < group->NumOfConnected()) {
+ /* Second device got just paired. We need to reconfigure CIG */
+ stream_conf->reconfiguration_ongoing = true;
+ groupStateMachine_->StopStream(group);
return;
}
+ /* Second device got reconnect. Try to get it to the stream seamlessly */
le_audio::types::AudioLocations sink_group_audio_locations = 0;
uint8_t sink_num_of_active_ases = 0;
@@ -1785,7 +1811,7 @@ class LeAudioClientImpl : public LeAudioClient {
}
void get_mono_stream(const std::vector<uint8_t>& data,
- std::vector<int16_t>& chan_mono) {
+ std::vector<int16_t>& chan_mono, int pitch = 1) {
uint16_t num_of_frames_per_ch;
int dt_us = current_source_codec_config.data_interval_us;
@@ -1793,7 +1819,7 @@ class LeAudioClientImpl : public LeAudioClient {
num_of_frames_per_ch = lc3_frame_samples(dt_us, sr_hz);
chan_mono.reserve(num_of_frames_per_ch);
- for (int i = 0; i < num_of_frames_per_ch; i++) {
+ for (int i = 0; i < pitch * num_of_frames_per_ch; i += pitch) {
const uint8_t* sample = data.data() + i * 4;
int16_t left = (int16_t)((*(sample + 1) << 8) + *sample) >> 1;
@@ -1807,37 +1833,6 @@ class LeAudioClientImpl : public LeAudioClient {
}
}
- void get_left_and_right_stream(const std::vector<uint8_t>& data,
- std::vector<int16_t>& chan_left,
- std::vector<int16_t>& chan_right,
- bool prepare_mono = false) {
- uint16_t num_of_frames_per_ch;
-
- int dt_us = current_source_codec_config.data_interval_us;
- int sr_hz = current_source_codec_config.sample_rate;
- num_of_frames_per_ch = lc3_frame_samples(dt_us, sr_hz);
-
- chan_left.reserve(num_of_frames_per_ch);
- chan_right.reserve(num_of_frames_per_ch);
- for (int i = 0; i < num_of_frames_per_ch; i++) {
- const uint8_t* sample = data.data() + i * 4;
-
- int16_t left = (int16_t)((*(sample + 1) << 8) + *sample) >> 1;
-
- sample += 2;
- int16_t right = (int16_t)((*(sample + 1) << 8) + *sample) >> 1;
-
- if (prepare_mono) {
- uint16_t mono_data = (int16_t)(((uint32_t)left + (uint32_t)right) >> 1);
- left = mono_data;
- right = mono_data;
- }
-
- chan_left.push_back(left);
- chan_right.push_back(right);
- }
- }
-
void PrepareAndSendToTwoDevices(
const std::vector<uint8_t>& data,
struct le_audio::stream_configuration* stream_conf) {
@@ -1869,27 +1864,37 @@ class LeAudioClientImpl : public LeAudioClient {
std::vector<uint8_t> chan_right_enc(byte_count, 0);
bool mono = (left_cis_handle == 0) || (right_cis_handle == 0);
+
+ int af_hz = audio_framework_source_config.sample_rate;
+ LOG_ASSERT(af_hz >= sr_hz) << __func__ << " sample freq issue";
+
+ int pitch = af_hz / sr_hz;
+
+ LOG(INFO) << __func__ << " pitch " << pitch
+ << " data size: " << (int)data.size()
+ << " byte count: " << byte_count << " mono: " << mono;
if (!mono) {
- lc3_encode(lc3_encoder_left, (const int16_t*)data.data(), 2,
+ lc3_encode(lc3_encoder_left, (const int16_t*)data.data(), 2 * pitch,
chan_left_enc.size(), chan_left_enc.data());
- lc3_encode(lc3_encoder_right, ((const int16_t*)data.data()) + 1, 2,
- chan_right_enc.size(), chan_right_enc.data());
+ lc3_encode(lc3_encoder_right, ((const int16_t*)data.data()) + 1,
+ 2 * pitch, chan_right_enc.size(), chan_right_enc.data());
} else {
- std::vector<int16_t> chan_left;
- std::vector<int16_t> chan_right;
- get_left_and_right_stream(data, chan_left, chan_right, mono);
+ std::vector<int16_t> chan_mono;
+ get_mono_stream(data, chan_mono, pitch);
if (left_cis_handle) {
- lc3_encode(lc3_encoder_left, (const int16_t*)chan_left.data(), 1,
+ lc3_encode(lc3_encoder_left, (const int16_t*)chan_mono.data(), 1,
chan_left_enc.size(), chan_left_enc.data());
}
if (right_cis_handle) {
- lc3_encode(lc3_encoder_right, (const int16_t*)chan_right.data(), 1,
+ lc3_encode(lc3_encoder_right, (const int16_t*)chan_mono.data(), 1,
chan_right_enc.size(), chan_right_enc.data());
}
}
+ DLOG(INFO) << __func__ << " left_cis_handle: " << +left_cis_handle
+ << " right_cis_handle: " << right_cis_handle;
/* Send data to the controller */
if (left_cis_handle)
IsoManager::GetInstance()->SendIsoData(
@@ -1919,23 +1924,24 @@ class LeAudioClientImpl : public LeAudioClient {
}
std::vector<uint8_t> chan_encoded(num_channels * byte_count, 0);
+ int af_hz = audio_framework_source_config.sample_rate;
+ LOG_ASSERT(af_hz >= sr_hz) << __func__ << " sample freq issue";
+
+ int pitch = af_hz / sr_hz;
+
if (num_channels == 1) {
/* Since we always get two channels from framework, lets make it mono here
*/
std::vector<int16_t> chan_mono;
- get_mono_stream(data, chan_mono);
+ get_mono_stream(data, chan_mono, pitch);
lc3_encode(lc3_encoder_left, (const int16_t*)chan_mono.data(), 1,
byte_count, chan_encoded.data());
} else {
- std::vector<int16_t> chan_left;
- std::vector<int16_t> chan_right;
- get_left_and_right_stream(data, chan_left, chan_right, false);
-
- lc3_encode(lc3_encoder_left, (const int16_t*)chan_left.data(), 1,
+ lc3_encode(lc3_encoder_left, (const int16_t*)data.data(), 2 * pitch,
byte_count, chan_encoded.data());
- lc3_encode(lc3_encoder_right, (const int16_t*)chan_right.data(), 1,
+ lc3_encode(lc3_encoder_right, (const int16_t*)data.data() + 1, 2 * pitch,
byte_count, chan_encoded.data() + byte_count);
}
@@ -2083,7 +2089,8 @@ class LeAudioClientImpl : public LeAudioClient {
/* TODO: What to do if not all data sinked ? */
if (written != to_write) LOG(ERROR) << __func__ << ", not all data sinked";
- LOG(INFO) << __func__;
+ DLOG(INFO) << __func__
+ << " num of frames: " << (int)lc3_decoder->lc3Config.NF;
}
static inline Lc3Config::FrameDuration Lc3ConfigFrameDuration(
@@ -2236,10 +2243,7 @@ class LeAudioClientImpl : public LeAudioClient {
}
}
- void StopAudio(void) {
- SuspendAudio();
- ClientAudioIntefraceRelease();
- }
+ void StopAudio(void) { SuspendAudio(); }
void printSingleConfiguration(int fd, LeAudioCodecConfiguration* conf,
bool print_audio_state, bool sender = false) {
@@ -2261,14 +2265,26 @@ class LeAudioClientImpl : public LeAudioClient {
}
void printCurrentStreamConfiguration(int fd) {
- auto conf = &current_source_codec_config;
- dprintf(fd, " Speaker codec config \n");
+ auto conf = &audio_framework_source_config;
+ dprintf(fd, " Speaker codec config (audio framework) \n");
if (conf) {
printSingleConfiguration(fd, conf, false);
}
- dprintf(fd, " Microphone codec config \n");
+ dprintf(fd, " Microphone codec config (audio framework) \n");
+ conf = &audio_framework_sink_config;
+ if (conf) {
+ printSingleConfiguration(fd, conf, false);
+ }
+
+ conf = &current_source_codec_config;
+ dprintf(fd, " Speaker codec config (Bluetooth)\n");
+ if (conf) {
+ printSingleConfiguration(fd, conf, true, true);
+ }
+
conf = &current_sink_codec_config;
+ dprintf(fd, " Microphone codec config (Bluetooth)\n");
if (conf) {
printSingleConfiguration(fd, conf, true, false);
}
@@ -2277,7 +2293,6 @@ class LeAudioClientImpl : public LeAudioClient {
void Dump(int fd) {
dprintf(fd, " Active group: %d\n", active_group_id_);
dprintf(fd, " current content type: 0x%08hx\n", current_context_type_);
- dprintf(fd, " upcoming content type: 0x%08hx\n", upcoming_context_type_);
dprintf(
fd, " stream setup time if started: %d ms\n",
(int)((stream_setup_end_timestamp_ - stream_setup_start_timestamp_) /
@@ -2298,20 +2313,16 @@ class LeAudioClientImpl : public LeAudioClient {
if (gatt_if_) BTA_GATTC_AppDeregister(gatt_if_);
}
- void UpdateCurrentHalSessions(int group_id, LeAudioContextType context_type) {
- if (group_id == bluetooth::groups::kGroupUnknown) {
- LOG(WARNING) << ", cannot start straming if no active group set";
- return;
- }
-
+ bool UpdateConfigAndCheckIfReconfigurationIsNeeded(
+ int group_id, LeAudioContextType context_type) {
+ bool reconfiguration_needed = false;
auto group = aseGroups_.FindById(group_id);
if (!group) {
LOG(ERROR) << __func__
<< ", Invalid group: " << static_cast<int>(group_id);
- return;
+ return reconfiguration_needed;
}
- bool send_active = false;
std::optional<LeAudioCodecConfiguration> source_configuration =
group->GetCodecConfigurationByDirection(
context_type, le_audio::types::kLeAudioDirectionSink);
@@ -2319,42 +2330,15 @@ class LeAudioClientImpl : public LeAudioClient {
group->GetCodecConfigurationByDirection(
context_type, le_audio::types::kLeAudioDirectionSource);
- if (!sink_configuration) {
- /* Let's check if le_audio group supports conversational, if so,
- * expose DECODED session to the system
- */
- sink_configuration = group->GetCodecConfigurationByDirection(
- LeAudioContextType::CONVERSATIONAL,
- le_audio::types::kLeAudioDirectionSource);
- if (sink_configuration) {
- LOG(INFO) << __func__
- << " exposing DECODED session to the system even context: "
- << static_cast<int>(context_type) << " does not use it";
- }
- }
-
if (source_configuration) {
- /* Stream configuration differs from previous one */
- if (!current_source_codec_config.IsInvalid() &&
- (*source_configuration != current_source_codec_config)) {
- callbacks_->OnGroupStatus(group_id, GroupStatus::INACTIVE);
- send_active = true;
- LeAudioClientAudioSource::Stop();
+ if (*source_configuration != current_source_codec_config) {
+ current_source_codec_config = *source_configuration;
+ reconfiguration_needed = true;
}
-
- current_source_codec_config = *source_configuration;
-
- /*Let's always request 2 channels from the framework */
- auto audio_framework_configuration = current_source_codec_config;
- audio_framework_configuration.num_channels = 2;
-
- LeAudioClientAudioSource::Start(audio_framework_configuration,
- audioSinkReceiver);
-
} else {
if (!current_source_codec_config.IsInvalid()) {
- LeAudioClientAudioSource::Stop();
current_source_codec_config = {0, 0, 0, 0};
+ reconfiguration_needed = true;
}
LOG(INFO) << __func__
@@ -2364,24 +2348,14 @@ class LeAudioClientImpl : public LeAudioClient {
}
if (sink_configuration) {
- /* Stream configuration differs from previous one */
- if (!current_sink_codec_config.IsInvalid() &&
- (*sink_configuration != current_sink_codec_config)) {
- if (send_active == false) {
- callbacks_->OnGroupStatus(group_id, GroupStatus::INACTIVE);
- send_active = true;
- }
- LeAudioClientAudioSink::Stop();
+ if (*sink_configuration != current_sink_codec_config) {
+ current_sink_codec_config = *sink_configuration;
+ reconfiguration_needed = true;
}
-
- current_sink_codec_config = *sink_configuration;
-
- LeAudioClientAudioSink::Start(current_sink_codec_config,
- audioSourceReceiver);
} else {
if (!current_sink_codec_config.IsInvalid()) {
- LeAudioClientAudioSink::Stop();
current_sink_codec_config = {0, 0, 0, 0};
+ reconfiguration_needed = true;
}
LOG(INFO) << __func__
@@ -2390,24 +2364,20 @@ class LeAudioClientImpl : public LeAudioClient {
<< static_cast<int>(context_type);
}
- if (send_active) {
- callbacks_->OnGroupStatus(group_id, GroupStatus::ACTIVE);
+ if (reconfiguration_needed) {
+ LOG(INFO) << __func__
+ << " Session reconfiguration needed group: " << group->group_id_
+ << " for context type: " << static_cast<int>(context_type);
}
- current_context_type_ = upcoming_context_type_;
+
+ current_context_type_ = context_type;
+ return reconfiguration_needed;
}
bool OnAudioResume(LeAudioDeviceGroup* group) {
- if (upcoming_context_type_ != current_context_type_) {
- return false;
- }
-
- /* Even context type is same, we might need more audio channels e.g. because
- * new device got connected */
- if (ReconfigureHalSessionIfNeeded(group, current_context_type_)) {
- return false;
+ if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+ return true;
}
-
- /* TODO check if group already started streaming */
return InternalGroupStream(active_group_id_,
static_cast<uint16_t>(current_context_type_));
}
@@ -2498,8 +2468,6 @@ class LeAudioClientImpl : public LeAudioClient {
<< " audio_sender_state: " << audio_sender_state_ << "\n"
<< " current_context_type_: "
<< static_cast<int>(current_context_type_) << "\n"
- << " upcoming_context_type_: "
- << static_cast<int>(upcoming_context_type_) << "\n"
<< " group " << (group ? " exist " : " does not exist ") << "\n";
switch (audio_sender_state_) {
@@ -2530,8 +2498,16 @@ class LeAudioClientImpl : public LeAudioClient {
break;
case AudioState::RELEASING:
case AudioState::READY_TO_RELEASE:
+ /* If group is reconfiguring, reassing state and wait for
+ * the stream to be established
+ */
+ if (group->stream_conf.reconfiguration_ongoing) {
+ audio_sender_state_ = audio_receiver_state_;
+ return;
+ }
+ FALLTHROUGH;
default:
- LeAudioClientAudioSink::CancelStreamingRequest();
+ LeAudioClientAudioSource::CancelStreamingRequest();
break;
}
@@ -2628,8 +2604,6 @@ class LeAudioClientImpl : public LeAudioClient {
<< " audio_sender_state: " << audio_sender_state_ << "\n"
<< " current_context_type_: "
<< static_cast<int>(current_context_type_) << "\n"
- << " upcoming_context_type_: "
- << static_cast<int>(upcoming_context_type_) << "\n"
<< " group " << (group ? " exist " : " does not exist ") << "\n";
switch (audio_receiver_state_) {
@@ -2658,6 +2632,14 @@ class LeAudioClientImpl : public LeAudioClient {
break;
case AudioState::RELEASING:
case AudioState::READY_TO_RELEASE:
+ /* If group is reconfiguring, reassing state and wait for
+ * the stream to be established
+ */
+ if (group->stream_conf.reconfiguration_ongoing) {
+ audio_receiver_state_ = audio_sender_state_;
+ return;
+ }
+ FALLTHROUGH;
default:
LeAudioClientAudioSink::CancelStreamingRequest();
break;
@@ -2767,55 +2749,26 @@ class LeAudioClientImpl : public LeAudioClient {
return available_contents[0];
}
- bool ReconfigureHalSessionIfNeeded(LeAudioDeviceGroup* group,
- LeAudioContextType new_context_type) {
- std::optional<LeAudioCodecConfiguration> source_configuration =
- group->GetCodecConfigurationByDirection(
- new_context_type, le_audio::types::kLeAudioDirectionSink);
-
- std::optional<LeAudioCodecConfiguration> sink_configuration =
- group->GetCodecConfigurationByDirection(
- new_context_type, le_audio::types::kLeAudioDirectionSource);
-
- if (!sink_configuration) {
- /* Let's check if le_audio group supports conversational, if so,
- * expose DECODED session to the system
- */
- sink_configuration = group->GetCodecConfigurationByDirection(
- LeAudioContextType::CONVERSATIONAL,
- le_audio::types::kLeAudioDirectionSource);
- if (sink_configuration) {
- DLOG(INFO) << __func__
- << " exposing DECODED session to the system even context: "
- << static_cast<int>(new_context_type) << " does not use it";
- }
+ bool StopStreamIfNeeded(LeAudioDeviceGroup* group,
+ LeAudioContextType new_context_type) {
+ DLOG(INFO) << __func__ << " context type " << int(new_context_type);
+ if (!UpdateConfigAndCheckIfReconfigurationIsNeeded(group->group_id_,
+ new_context_type)) {
+ DLOG(INFO) << __func__ << " reconfiguration not needed";
+ return false;
}
- if ((source_configuration &&
- (*source_configuration != current_source_codec_config)) ||
- (sink_configuration &&
- (*sink_configuration != current_sink_codec_config))) {
- LOG(INFO) << __func__
- << " Session reconfiguration needed group: " << group->group_id_
- << "for context type: " << static_cast<int>(new_context_type);
-
- if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
- if (alarm_is_scheduled(suspend_timeout_))
- alarm_cancel(suspend_timeout_);
-
- GroupStop(group->group_id_);
- }
-
- upcoming_context_type_ = new_context_type;
- /* Schedule HAL Session update */
- do_in_main_thread(FROM_HERE,
- base::Bind(&LeAudioClientImpl::UpdateCurrentHalSessions,
- base::Unretained(instance), group->group_id_,
- upcoming_context_type_));
- return true;
+ if (group->GetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+ DLOG(INFO) << __func__ << " Group is not streaming ";
+ return false;
}
- return false;
+ if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);
+
+ /* Need to reconfigure stream */
+ group->stream_conf.reconfiguration_ongoing = true;
+ GroupStop(group->group_id_);
+ return true;
}
void OnAudioMetadataUpdate(const source_metadata_t& source_metadata) {
@@ -2869,14 +2822,17 @@ class LeAudioClientImpl : public LeAudioClient {
return;
}
- if (ReconfigureHalSessionIfNeeded(group, new_context)) {
+ current_context_type_ = new_context;
+ if (StopStreamIfNeeded(group, new_context)) {
return;
}
- /* Configuration is the same for new context, just will do update
- * metadata of stream
- */
- GroupStream(active_group_id_, static_cast<uint16_t>(new_context));
+ if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+ /* Configuration is the same for new context, just will do update
+ * metadata of stream
+ */
+ GroupStream(active_group_id_, static_cast<uint16_t>(new_context));
+ }
}
static void OnGattReadRspStatic(uint16_t conn_id, tGATT_STATUS status,
@@ -3021,7 +2977,39 @@ class LeAudioClientImpl : public LeAudioClient {
rxUnreceivedPackets, duplicatePackets);
}
+ bool RestartStreamingAfterReconfiguration(int group_id) {
+ if (group_id != active_group_id_) return false;
+
+ DLOG(INFO) << __func__ << " audio_sender_state_: " << audio_sender_state_
+ << " audio_receiver_state_: " << audio_receiver_state_;
+
+ auto group = aseGroups_.FindById(group_id);
+ if (!group) return false;
+
+ auto stream_conf = &group->stream_conf;
+ DLOG(INFO) << __func__ << " stream_conf->reconfiguration_ongoing "
+ << stream_conf->reconfiguration_ongoing;
+
+ if (!stream_conf->reconfiguration_ongoing) return false;
+
+ if (!groupStateMachine_->StartStream(
+ group, static_cast<LeAudioContextType>(current_context_type_)))
+ return false;
+
+ if (audio_sender_state_ == AudioState::RELEASING)
+ audio_sender_state_ = AudioState::READY_TO_START;
+
+ if (audio_receiver_state_ == AudioState::RELEASING)
+ audio_receiver_state_ = AudioState::READY_TO_START;
+
+ stream_conf->reconfiguration_ongoing = false;
+ return true;
+ }
+
void StatusReportCb(int group_id, GroupStreamStatus status) {
+ DLOG(INFO) << __func__ << "status: " << static_cast<int>(status)
+ << " audio_sender_state_: " << audio_sender_state_
+ << " audio_receiver_state_: " << audio_receiver_state_;
switch (status) {
case GroupStreamStatus::STREAMING:
if (audio_sender_state_ == AudioState::READY_TO_START)
@@ -3036,15 +3024,23 @@ class LeAudioClientImpl : public LeAudioClient {
/** Stop Audio but don't release all the Audio resources */
SuspendAudio();
break;
- case GroupStreamStatus::IDLE:
+ case GroupStreamStatus::IDLE: {
stream_setup_end_timestamp_ = 0;
stream_setup_start_timestamp_ = 0;
- CancelStreamingRequest();
+
+ if (!RestartStreamingAfterReconfiguration(group_id))
+ CancelStreamingRequest();
+
break;
+ }
case GroupStreamStatus::RELEASING:
case GroupStreamStatus::SUSPENDING:
- audio_sender_state_ = AudioState::RELEASING;
- audio_receiver_state_ = AudioState::RELEASING;
+ if (audio_sender_state_ != AudioState::IDLE)
+ audio_sender_state_ = AudioState::RELEASING;
+
+ if (audio_receiver_state_ != AudioState::IDLE)
+ audio_receiver_state_ = AudioState::RELEASING;
+
break;
default:
break;
@@ -3059,7 +3055,6 @@ class LeAudioClientImpl : public LeAudioClient {
LeAudioGroupStateMachine* groupStateMachine_;
int active_group_id_;
LeAudioContextType current_context_type_;
- LeAudioContextType upcoming_context_type_;
uint64_t stream_setup_start_timestamp_;
uint64_t stream_setup_end_timestamp_;
@@ -3068,9 +3063,27 @@ class LeAudioClientImpl : public LeAudioClient {
/* Speaker(s) */
AudioState audio_sender_state_;
+ /* Current stream configuration */
LeAudioCodecConfiguration current_source_codec_config;
LeAudioCodecConfiguration current_sink_codec_config;
+ /* Static Audio Framework session configuration.
+ * Resampling will be done inside the bt stack
+ */
+ LeAudioCodecConfiguration audio_framework_source_config = {
+ .num_channels = 2,
+ .sample_rate = bluetooth::audio::le_audio::kSampleRate48000,
+ .bits_per_sample = bluetooth::audio::le_audio::kBitsPerSample16,
+ .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us,
+ };
+
+ LeAudioCodecConfiguration audio_framework_sink_config = {
+ .num_channels = 1,
+ .sample_rate = bluetooth::audio::le_audio::kSampleRate16000,
+ .bits_per_sample = bluetooth::audio::le_audio::kBitsPerSample16,
+ .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us,
+ };
+
void* lc3_encoder_left_mem;
void* lc3_encoder_right_mem;
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index 314cbeaed3..40588f6f45 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -1187,6 +1187,8 @@ void LeAudioDeviceGroup::Dump(int fd) {
<< " active stream configuration name: "
<< (active_conf ? active_conf->name : " not set") << "\n"
<< " Last used stream configuration: \n"
+ << " reconfiguration_ongoing: "
+ << stream_conf.reconfiguration_ongoing << "\n"
<< " codec id : " << +(stream_conf.id.coding_format) << "\n"
<< " name: "
<< (stream_conf.conf != nullptr ? stream_conf.conf->name : " null ")
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index b9f4d07e03..29f362b9d5 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -538,13 +538,21 @@ class UnicastTestNoInit : public Test {
group->SetTargetState(
types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
group->SetState(group->GetTargetState());
- state_machine_callbacks_->StatusReportCb(
- group->group_id_, GroupStreamStatus::STREAMING);
streaming_groups[group->group_id_] = group;
/* Assume CIG is created */
group->cig_created_ = true;
+ do_in_main_thread(
+ FROM_HERE, base::BindOnce(
+ [](int group_id,
+ le_audio::LeAudioGroupStateMachine::Callbacks*
+ state_machine_callbacks) {
+ state_machine_callbacks->StatusReportCb(
+ group_id, GroupStreamStatus::STREAMING);
+ },
+ group->group_id_,
+ base::Unretained(this->state_machine_callbacks_)));
return true;
});
@@ -670,6 +678,8 @@ class UnicastTestNoInit : public Test {
// Inject the state
group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
group->SetState(group->GetTargetState());
+ state_machine_callbacks_->StatusReportCb(
+ group->group_id_, GroupStreamStatus::RELEASING);
state_machine_callbacks_->StatusReportCb(group->group_id_,
GroupStreamStatus::IDLE);
});
@@ -964,16 +974,11 @@ class UnicastTestNoInit : public Test {
UpdateMetadata(usage, content_type);
- if (reconfigured_sink) {
- EXPECT_CALL(mock_audio_source_, CancelStreamingRequest()).Times(1);
- audio_sink_receiver_->OnAudioResume();
- }
- SyncOnMainLoop();
+ EXPECT_CALL(mock_audio_source_, ConfirmStreamingRequest()).Times(1);
audio_sink_receiver_->OnAudioResume();
- EXPECT_CALL(mock_audio_source_, ConfirmStreamingRequest()).Times(1);
- state_machine_callbacks_->StatusReportCb(group_id,
- GroupStreamStatus::STREAMING);
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_source_);
if (usage == AUDIO_USAGE_VOICE_COMMUNICATION) {
ASSERT_NE(audio_source_receiver_, nullptr);
@@ -2256,7 +2261,9 @@ TEST_F(UnicastTest, RemoveWhileStreaming) {
uint8_t cis_count_out = 1;
uint8_t cis_count_in = 0;
+ // Audio sessions are started only when device gets active
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
EXPECT_CALL(mock_state_machine_, StartStream(_, _)).Times(1);
@@ -2319,7 +2326,9 @@ TEST_F(UnicastTest, SpeakerStreaming) {
uint8_t cis_count_out = 1;
uint8_t cis_count_in = 0;
+ // Audio sessions are started only when device gets active
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2349,6 +2358,7 @@ TEST_F(UnicastTest, SpeakerStreaming) {
// Release
EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
EXPECT_CALL(mock_audio_source_, Release(_)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Release(_)).Times(1);
LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
}
@@ -2374,6 +2384,7 @@ TEST_F(UnicastTest, SpeakerStreamingAutonomousRelease) {
// Start streaming
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2429,17 +2440,14 @@ TEST_F(UnicastTest, TwoEarbudsStreaming) {
codec_spec_conf::kLeAudioLocationFrontRight, group_size,
group_id, 2 /* rank*/, true /*connect_through_csis*/);
+ // Start streaming
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
- // Start streaming with reconfiguration from default media stream setup
- EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
- EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
- EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
-
StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
- group_id, true /* reconfigure */);
+ group_id);
Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2449,17 +2457,11 @@ TEST_F(UnicastTest, TwoEarbudsStreaming) {
// Verify Data transfer on two peer sinks and one source
uint8_t cis_count_out = 2;
uint8_t cis_count_in = 1;
- TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 640);
+ TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
// Suspend
- EXPECT_CALL(mock_audio_source_, Release(_)).Times(0);
- EXPECT_CALL(mock_audio_sink_, Release(_)).Times(0);
- EXPECT_CALL(mock_audio_source_, Stop()).Times(0);
- EXPECT_CALL(mock_audio_sink_, Stop()).Times(0);
LeAudioClient::Get()->GroupSuspend(group_id);
SyncOnMainLoop();
- Mock::VerifyAndClearExpectations(&mock_audio_source_);
- Mock::VerifyAndClearExpectations(&mock_audio_sink_);
// Resume
StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
@@ -2469,7 +2471,7 @@ TEST_F(UnicastTest, TwoEarbudsStreaming) {
Mock::VerifyAndClearExpectations(&mock_audio_sink_);
// Verify Data transfer still works
- TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 640);
+ TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
// Stop
StopStreaming(group_id, true);
@@ -2511,7 +2513,9 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchSimple) {
codec_spec_conf::kLeAudioLocationFrontRight, group_size,
group_id, 2 /* rank*/, true /*connect_through_csis*/);
+ // Start streaming
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2520,11 +2524,9 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchSimple) {
mock_state_machine_,
StartStream(_, le_audio::types::LeAudioContextType::NOTIFICATIONS))
.Times(1);
- EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
- EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
- StartStreaming(AUDIO_USAGE_NOTIFICATION, AUDIO_CONTENT_TYPE_UNKNOWN, group_id,
- true /* reconfigure */);
+ StartStreaming(AUDIO_USAGE_NOTIFICATION, AUDIO_CONTENT_TYPE_UNKNOWN,
+ group_id);
Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2582,6 +2584,7 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) {
// Start streaming MEDIA
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2599,14 +2602,8 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) {
StopStreaming(group_id);
Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
- // Start streaming
- EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
- EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
- EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
- LeAudioClient::Get()->GroupSetActive(group_id);
-
StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
- group_id, true /* reconfigure */);
+ group_id);
Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2616,7 +2613,7 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) {
// Verify Data transfer on two peer sinks and one source
cis_count_out = 2;
cis_count_in = 1;
- TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 640);
+ TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
}
TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) {
@@ -2636,6 +2633,7 @@ TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) {
// Start streaming
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2656,11 +2654,7 @@ TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) {
codec_spec_conf::kLeAudioLocationFrontRight, group_size,
group_id, 2 /* rank*/, true /*connect_through_csis*/);
- /* We should expect two iso channels to be fed with data, but for now, when
- * second device is connected later, we just continue stream to one device.
- * TODO: improve it.
- */
- cis_count_out = 1;
+ cis_count_out = 2;
cis_count_in = 0;
TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
}
@@ -2687,8 +2681,9 @@ TEST_F(UnicastTest, TwoEarbuds2ndDisconnect) {
codec_spec_conf::kLeAudioLocationFrontRight, group_size,
group_id, 2 /* rank*/, true /*connect_through_csis*/);
- // Start streaming
+ // Audio sessions are started only when device gets active
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index 111504f9d7..c74e8ecbc7 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -88,6 +88,11 @@ bool check_if_may_cover_scenario(const AudioSetConfiguration* audio_set_conf,
return group_size >= min_req_devices_cnt(audio_set_conf);
}
+uint8_t get_num_of_devices_in_configuration(
+ const AudioSetConfiguration* audio_set_conf) {
+ return min_req_devices_cnt(audio_set_conf);
+}
+
static bool IsCodecConfigurationSupported(const types::LeAudioLtvMap& pacs,
const LeAudioLc3Config& lc3_config) {
const auto& reqs = lc3_config.GetAsLtvMap();
diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h
index 6371696f17..48b6901d3d 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -952,9 +952,12 @@ bool IsCodecCapabilitySettingSupported(
const types::acs_ac_record& pac_record,
const CodecCapabilitySetting& codec_capability_setting);
const AudioSetConfigurations* get_confs_by_type(types::LeAudioContextType type);
+uint8_t get_num_of_devices_in_configuration(
+ const AudioSetConfiguration* audio_set_configuration);
} // namespace set_configurations
struct stream_configuration {
+ bool reconfiguration_ongoing;
types::LeAudioCodecId id;