blob: e2a5dc8ca45bdd220c03425915e079cb56cae7d2 [file] [log] [blame]
/*
* 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 "broadcaster_types.h"
#include <vector>
#include "bt_types.h"
#include "bta_le_audio_broadcaster_api.h"
#include "btm_ble_api_types.h"
#include "embdrv/lc3/include/lc3.h"
#include "internal_include/stack_config.h"
#include "osi/include/properties.h"
using bluetooth::le_audio::BasicAudioAnnouncementBisConfig;
using bluetooth::le_audio::BasicAudioAnnouncementCodecConfig;
using bluetooth::le_audio::BasicAudioAnnouncementData;
using bluetooth::le_audio::BasicAudioAnnouncementSubgroup;
using le_audio::types::LeAudioContextType;
namespace le_audio {
namespace broadcaster {
static void EmitHeader(const BasicAudioAnnouncementData& announcement_data,
std::vector<uint8_t>& data) {
size_t old_size = data.size();
data.resize(old_size + 3);
// Set the cursor behind the old data
uint8_t* p_value = data.data() + old_size;
UINT24_TO_STREAM(p_value, announcement_data.presentation_delay);
}
static void EmitCodecConfiguration(
const BasicAudioAnnouncementCodecConfig& config, std::vector<uint8_t>& data,
const BasicAudioAnnouncementCodecConfig* lower_lvl_config) {
size_t old_size = data.size();
// Add 5 for full, or 1 for short Codec ID
uint8_t codec_config_length = 5;
auto ltv = types::LeAudioLtvMap(config.codec_specific_params);
auto ltv_raw_sz = ltv.RawPacketSize();
// Add 1 for the codec spec. config length + config spec. data itself
codec_config_length += 1 + ltv_raw_sz;
// Resize and set the cursor behind the old data
data.resize(old_size + codec_config_length);
uint8_t* p_value = data.data() + old_size;
// Codec ID
UINT8_TO_STREAM(p_value, config.codec_id);
UINT16_TO_STREAM(p_value, config.vendor_company_id);
UINT16_TO_STREAM(p_value, config.vendor_codec_id);
// Codec specific config length and data
UINT8_TO_STREAM(p_value, ltv_raw_sz);
p_value = ltv.RawPacket(p_value);
}
static void EmitMetadata(
const std::map<uint8_t, std::vector<uint8_t>>& metadata,
std::vector<uint8_t>& data) {
auto ltv = types::LeAudioLtvMap(metadata);
auto ltv_raw_sz = ltv.RawPacketSize();
size_t old_size = data.size();
data.resize(old_size + ltv_raw_sz + 1);
// Set the cursor behind the old data
uint8_t* p_value = data.data() + old_size;
UINT8_TO_STREAM(p_value, ltv_raw_sz);
if (ltv_raw_sz > 0) {
p_value = ltv.RawPacket(p_value);
}
}
static void EmitBroadcastName(const std::string& name,
std::vector<uint8_t>& data) {
int name_len = name.length();
size_t old_size = data.size();
data.resize(old_size + name_len + 2);
// Set the cursor behind the old data
uint8_t* p_value = data.data() + old_size;
UINT8_TO_STREAM(p_value, name_len + 1);
UINT8_TO_STREAM(p_value, BTM_BLE_AD_TYPE_BROADCAST_NAME);
std::vector<uint8_t> vec(name.begin(), name.end());
ARRAY_TO_STREAM(p_value, vec.data(), name_len);
}
static void EmitBisConfigs(
const std::vector<BasicAudioAnnouncementBisConfig>& bis_configs,
std::vector<uint8_t>& data) {
// Emit each BIS config - that's the level 3 data
for (auto const& bis_config : bis_configs) {
auto ltv = types::LeAudioLtvMap(bis_config.codec_specific_params);
auto ltv_raw_sz = ltv.RawPacketSize();
size_t old_size = data.size();
data.resize(old_size + ltv_raw_sz + 2);
// Set the cursor behind the old data
auto* p_value = data.data() + old_size;
// BIS_index[i[k]]
UINT8_TO_STREAM(p_value, bis_config.bis_index);
// Per BIS Codec Specific Params[i[k]]
UINT8_TO_STREAM(p_value, ltv_raw_sz);
if (ltv_raw_sz > 0) {
p_value = ltv.RawPacket(p_value);
}
}
}
static void EmitSubgroup(const BasicAudioAnnouncementSubgroup& subgroup_config,
std::vector<uint8_t>& data) {
// That's the level 2 data
// Resize for the num_bis
size_t initial_offset = data.size();
data.resize(initial_offset + 1);
// Set the cursor behind the old data and adds the level 2 Num_BIS[i]
uint8_t* p_value = data.data() + initial_offset;
UINT8_TO_STREAM(p_value, subgroup_config.bis_configs.size());
EmitCodecConfiguration(subgroup_config.codec_config, data, nullptr);
EmitMetadata(subgroup_config.metadata, data);
// This adds the level 3 data
EmitBisConfigs(subgroup_config.bis_configs, data);
}
bool ToRawPacket(BasicAudioAnnouncementData const& in,
std::vector<uint8_t>& data) {
EmitHeader(in, data);
// Set the cursor behind the old data and resize
size_t old_size = data.size();
data.resize(old_size + 1);
uint8_t* p_value = data.data() + old_size;
// Emit the subgroup size and each subgroup
// That's the level 1 Num_Subgroups
UINT8_TO_STREAM(p_value, in.subgroup_configs.size());
for (const auto& subgroup_config : in.subgroup_configs) {
// That's the level 2 and higher level data
EmitSubgroup(subgroup_config, data);
}
return true;
}
void PrepareAdvertisingData(
bool is_public, const std::string& broadcast_name,
bluetooth::le_audio::BroadcastId& broadcast_id,
const bluetooth::le_audio::PublicBroadcastAnnouncementData&
public_announcement,
std::vector<uint8_t>& adv_data) {
adv_data.resize(7);
uint8_t* data_ptr = adv_data.data();
UINT8_TO_STREAM(data_ptr, 6);
UINT8_TO_STREAM(data_ptr, BTM_BLE_AD_TYPE_SERVICE_DATA_TYPE);
UINT16_TO_STREAM(data_ptr, kBroadcastAudioAnnouncementServiceUuid);
UINT24_TO_STREAM(data_ptr, broadcast_id);
// Prepare public broadcast announcement data
if (is_public) {
size_t old_size = adv_data.size();
// 5: datalen(1) + adtype(1) + serviceuuid(2) + features(1)
adv_data.resize(old_size + 5);
// Skip the data length field until the full content is generated
data_ptr = adv_data.data() + old_size + 1;
UINT8_TO_STREAM(data_ptr, BTM_BLE_AD_TYPE_SERVICE_DATA_TYPE);
UINT16_TO_STREAM(data_ptr, kPublicBroadcastAnnouncementServiceUuid);
UINT8_TO_STREAM(data_ptr, public_announcement.features);
// Set metadata length to 0 if no meta data present
EmitMetadata(public_announcement.metadata, adv_data);
// Update the length field accordingly
data_ptr = adv_data.data() + old_size;
UINT8_TO_STREAM(data_ptr, adv_data.size() - old_size - 1);
// Prepare broadcast name
if (!broadcast_name.empty()) {
EmitBroadcastName(broadcast_name, adv_data);
}
}
}
void PreparePeriodicData(const BasicAudioAnnouncementData& announcement,
std::vector<uint8_t>& periodic_data) {
/* Account for AD Type + Service UUID */
periodic_data.resize(4);
/* Skip the data length field until the full content is generated */
uint8_t* data_ptr = periodic_data.data() + 1;
UINT8_TO_STREAM(data_ptr, BTM_BLE_AD_TYPE_SERVICE_DATA_TYPE);
UINT16_TO_STREAM(data_ptr, kBasicAudioAnnouncementServiceUuid);
/* Append the announcement */
ToRawPacket(announcement, periodic_data);
/* Update the length field accordingly */
data_ptr = periodic_data.data();
UINT8_TO_STREAM(data_ptr, periodic_data.size() - 1);
}
constexpr types::LeAudioCodecId kLeAudioCodecIdLc3 = {
.coding_format = types::kLeAudioCodingFormatLC3,
.vendor_company_id = types::kLeAudioVendorCompanyIdUndefined,
.vendor_codec_id = types::kLeAudioVendorCodecIdUndefined};
static const BroadcastCodecWrapper lc3_mono_16_2 = BroadcastCodecWrapper(
kLeAudioCodecIdLc3,
// LeAudioCodecConfiguration
{.num_channels = LeAudioCodecConfiguration::kChannelNumberMono,
.sample_rate = LeAudioCodecConfiguration::kSampleRate16000,
.bits_per_sample = LeAudioCodecConfiguration::kBitsPerSample16,
.data_interval_us = LeAudioCodecConfiguration::kInterval10000Us},
// Bitrate
32000,
// Frame len.
40);
static const BroadcastCodecWrapper lc3_stereo_16_2 = BroadcastCodecWrapper(
kLeAudioCodecIdLc3,
// LeAudioCodecConfiguration
{.num_channels = LeAudioCodecConfiguration::kChannelNumberStereo,
.sample_rate = LeAudioCodecConfiguration::kSampleRate16000,
.bits_per_sample = LeAudioCodecConfiguration::kBitsPerSample16,
.data_interval_us = LeAudioCodecConfiguration::kInterval10000Us},
// Bitrate
32000,
// Frame len.
40);
static const BroadcastCodecWrapper lc3_stereo_24_2 = BroadcastCodecWrapper(
kLeAudioCodecIdLc3,
// LeAudioCodecConfiguration
{.num_channels = LeAudioCodecConfiguration::kChannelNumberStereo,
.sample_rate = LeAudioCodecConfiguration::kSampleRate24000,
.bits_per_sample = LeAudioCodecConfiguration::kBitsPerSample16,
.data_interval_us = LeAudioCodecConfiguration::kInterval10000Us},
// Bitrate
48000,
// Frame len.
60);
static const BroadcastCodecWrapper lc3_stereo_48_1 = BroadcastCodecWrapper(
kLeAudioCodecIdLc3,
// LeAudioCodecConfiguration
{.num_channels = LeAudioCodecConfiguration::kChannelNumberStereo,
.sample_rate = LeAudioCodecConfiguration::kSampleRate48000,
.bits_per_sample = LeAudioCodecConfiguration::kBitsPerSample16,
.data_interval_us = LeAudioCodecConfiguration::kInterval7500Us},
// Bitrate
80000,
// Frame len.
75);
static const BroadcastCodecWrapper lc3_stereo_48_2 = BroadcastCodecWrapper(
kLeAudioCodecIdLc3,
// LeAudioCodecConfiguration
{.num_channels = LeAudioCodecConfiguration::kChannelNumberStereo,
.sample_rate = LeAudioCodecConfiguration::kSampleRate48000,
.bits_per_sample = LeAudioCodecConfiguration::kBitsPerSample16,
.data_interval_us = LeAudioCodecConfiguration::kInterval10000Us},
// Bitrate
80000,
// Frame len.
100);
static const BroadcastCodecWrapper lc3_stereo_48_3 = BroadcastCodecWrapper(
kLeAudioCodecIdLc3,
// LeAudioCodecConfiguration
{.num_channels = LeAudioCodecConfiguration::kChannelNumberStereo,
.sample_rate = LeAudioCodecConfiguration::kSampleRate48000,
.bits_per_sample = LeAudioCodecConfiguration::kBitsPerSample16,
.data_interval_us = LeAudioCodecConfiguration::kInterval7500Us},
// Bitrate
96000,
// Frame len.
90);
static const BroadcastCodecWrapper lc3_stereo_48_4 = BroadcastCodecWrapper(
kLeAudioCodecIdLc3,
// LeAudioCodecConfiguration
{.num_channels = LeAudioCodecConfiguration::kChannelNumberStereo,
.sample_rate = LeAudioCodecConfiguration::kSampleRate48000,
.bits_per_sample = LeAudioCodecConfiguration::kBitsPerSample16,
.data_interval_us = LeAudioCodecConfiguration::kInterval10000Us},
// Bitrate
96000,
// Frame len.
120);
const std::map<uint32_t, uint8_t> sample_rate_to_sampling_freq_map = {
{LeAudioCodecConfiguration::kSampleRate8000,
codec_spec_conf::kLeAudioSamplingFreq8000Hz},
{LeAudioCodecConfiguration::kSampleRate16000,
codec_spec_conf::kLeAudioSamplingFreq16000Hz},
{LeAudioCodecConfiguration::kSampleRate24000,
codec_spec_conf::kLeAudioSamplingFreq24000Hz},
{LeAudioCodecConfiguration::kSampleRate32000,
codec_spec_conf::kLeAudioSamplingFreq32000Hz},
{LeAudioCodecConfiguration::kSampleRate44100,
codec_spec_conf::kLeAudioSamplingFreq44100Hz},
{LeAudioCodecConfiguration::kSampleRate48000,
codec_spec_conf::kLeAudioSamplingFreq48000Hz},
};
const std::map<uint32_t, uint8_t> data_interval_ms_to_frame_duration = {
{LeAudioCodecConfiguration::kInterval7500Us,
codec_spec_conf::kLeAudioCodecLC3FrameDur7500us},
{LeAudioCodecConfiguration::kInterval10000Us,
codec_spec_conf::kLeAudioCodecLC3FrameDur10000us},
};
types::LeAudioLtvMap BroadcastCodecWrapper::GetBisCodecSpecData(
uint8_t bis_idx) const {
/* For a single channel this will be set at the subgroup lvl. */
if (source_codec_config.num_channels == 1) return {};
switch (bis_idx) {
case 1:
return types::LeAudioLtvMap(
{{codec_spec_conf::kLeAudioCodecLC3TypeAudioChannelAllocation,
UINT32_TO_VEC_UINT8(codec_spec_conf::kLeAudioLocationFrontLeft)}});
case 2:
return types::LeAudioLtvMap(
{{codec_spec_conf::kLeAudioCodecLC3TypeAudioChannelAllocation,
UINT32_TO_VEC_UINT8(codec_spec_conf::kLeAudioLocationFrontRight)}});
break;
default:
return {};
}
}
types::LeAudioLtvMap BroadcastCodecWrapper::GetSubgroupCodecSpecData() const {
LOG_ASSERT(
sample_rate_to_sampling_freq_map.count(source_codec_config.sample_rate))
<< "Invalid sample_rate";
LOG_ASSERT(data_interval_ms_to_frame_duration.count(
source_codec_config.data_interval_us))
<< "Invalid data_interval";
std::map<uint8_t, std::vector<uint8_t>> codec_spec_ltvs = {
{codec_spec_conf::kLeAudioCodecLC3TypeSamplingFreq,
UINT8_TO_VEC_UINT8(sample_rate_to_sampling_freq_map.at(
source_codec_config.sample_rate))},
{codec_spec_conf::kLeAudioCodecLC3TypeFrameDuration,
UINT8_TO_VEC_UINT8(data_interval_ms_to_frame_duration.at(
source_codec_config.data_interval_us))},
};
if (codec_id.coding_format == kLeAudioCodecIdLc3.coding_format) {
uint16_t bc =
lc3_frame_bytes(source_codec_config.data_interval_us, codec_bitrate);
codec_spec_ltvs[codec_spec_conf::kLeAudioCodecLC3TypeOctetPerFrame] =
UINT16_TO_VEC_UINT8(bc);
}
if (source_codec_config.num_channels == 1) {
codec_spec_ltvs
[codec_spec_conf::kLeAudioCodecLC3TypeAudioChannelAllocation] =
UINT32_TO_VEC_UINT8(codec_spec_conf::kLeAudioLocationFrontCenter);
}
return types::LeAudioLtvMap(codec_spec_ltvs);
}
std::ostream& operator<<(
std::ostream& os,
const le_audio::broadcaster::BroadcastCodecWrapper& config) {
os << " BroadcastCodecWrapper=[";
os << "CodecID="
<< "{" << +config.GetLeAudioCodecId().coding_format << ":"
<< +config.GetLeAudioCodecId().vendor_company_id << ":"
<< +config.GetLeAudioCodecId().vendor_codec_id << "}";
os << ", LeAudioCodecConfiguration="
<< "{NumChannels=" << +config.GetNumChannels()
<< ", SampleRate=" << +config.GetSampleRate()
<< ", BitsPerSample=" << +config.GetBitsPerSample()
<< ", DataIntervalUs=" << +config.GetDataIntervalUs() << "}";
os << ", Bitrate=" << +config.GetBitrate();
os << "]";
return os;
}
std::ostream& operator<<(
std::ostream& os, const le_audio::broadcaster::BroadcastQosConfig& config) {
os << " BroadcastQosConfig=[";
os << "RTN=" << +config.getRetransmissionNumber();
os << ", MaxTransportLatency=" << config.getMaxTransportLatency();
os << "]";
return os;
}
static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
lc3_mono_16_2_1 = {lc3_mono_16_2, qos_config_2_10};
static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
lc3_mono_16_2_2 = {lc3_mono_16_2, qos_config_4_60};
static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
lc3_stereo_16_2_2 = {lc3_stereo_16_2, qos_config_4_60};
static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
lc3_stereo_24_2_1 = {lc3_stereo_24_2, qos_config_2_10};
static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
lc3_stereo_24_2_2 = {lc3_stereo_24_2, qos_config_4_60};
static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
lc3_stereo_48_1_2 = {lc3_stereo_48_1, qos_config_4_50};
static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
lc3_stereo_48_2_2 = {lc3_stereo_48_2, qos_config_4_65};
static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
lc3_stereo_48_3_2 = {lc3_stereo_48_3, qos_config_4_50};
static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
lc3_stereo_48_4_2 = {lc3_stereo_48_4, qos_config_4_65};
std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
getStreamConfigForContext(types::AudioContexts context) {
const std::string* options =
stack_config_get_interface()->get_pts_broadcast_audio_config_options();
if (options) {
if (!options->compare("lc3_stereo_48_1_2")) return lc3_stereo_48_1_2;
if (!options->compare("lc3_stereo_48_2_2")) return lc3_stereo_48_2_2;
if (!options->compare("lc3_stereo_48_3_2")) return lc3_stereo_48_3_2;
if (!options->compare("lc3_stereo_48_4_2")) return lc3_stereo_48_4_2;
}
// High quality, Low Latency
if (context.test_any(LeAudioContextType::GAME | LeAudioContextType::LIVE))
return lc3_stereo_24_2_1;
// Low quality, Low Latency
if (context.test(LeAudioContextType::INSTRUCTIONAL)) return lc3_mono_16_2_1;
// Low quality, High Reliability
if (context.test_any(LeAudioContextType::SOUNDEFFECTS |
LeAudioContextType::UNSPECIFIED))
return lc3_stereo_16_2_2;
if (context.test_any(LeAudioContextType::ALERTS |
LeAudioContextType::NOTIFICATIONS |
LeAudioContextType::EMERGENCYALARM))
return lc3_mono_16_2_2;
// High quality, High Reliability
if (context.test(LeAudioContextType::MEDIA)) return lc3_stereo_24_2_2;
// Defaults: Low quality, High Reliability
return lc3_mono_16_2_2;
}
} /* namespace broadcaster */
} /* namespace le_audio */
/* Helper functions for comparing BroadcastAnnouncements */
namespace bluetooth {
namespace le_audio {
static bool isMetadataSame(std::map<uint8_t, std::vector<uint8_t>> m1,
std::map<uint8_t, std::vector<uint8_t>> m2) {
if (m1.size() != m2.size()) return false;
for (auto& m1pair : m1) {
if (m2.count(m1pair.first) == 0) return false;
auto& m2val = m2.at(m1pair.first);
if (m1pair.second.size() != m2val.size()) return false;
if (m1pair.second.size() != 0) {
if (memcmp(m1pair.second.data(), m2val.data(), m2val.size()) != 0)
return false;
}
}
return true;
}
bool operator==(const BasicAudioAnnouncementData& lhs,
const BasicAudioAnnouncementData& rhs) {
if (lhs.presentation_delay != rhs.presentation_delay) return false;
if (lhs.subgroup_configs.size() != rhs.subgroup_configs.size()) return false;
for (auto i = 0lu; i < lhs.subgroup_configs.size(); ++i) {
auto& lhs_subgroup = lhs.subgroup_configs[i];
auto& rhs_subgroup = rhs.subgroup_configs[i];
if (lhs_subgroup.codec_config.codec_id !=
rhs_subgroup.codec_config.codec_id)
return false;
if (lhs_subgroup.codec_config.vendor_company_id !=
rhs_subgroup.codec_config.vendor_company_id)
return false;
if (lhs_subgroup.codec_config.vendor_codec_id !=
rhs_subgroup.codec_config.vendor_codec_id)
return false;
if (!isMetadataSame(lhs_subgroup.codec_config.codec_specific_params,
rhs_subgroup.codec_config.codec_specific_params))
return false;
if (!isMetadataSame(lhs_subgroup.metadata, rhs_subgroup.metadata))
return false;
for (auto j = 0lu; j < lhs_subgroup.bis_configs.size(); ++j) {
auto& lhs_bis_config = lhs_subgroup.bis_configs[i];
auto& rhs_bis_config = rhs_subgroup.bis_configs[i];
if (lhs_bis_config.bis_index != rhs_bis_config.bis_index) return false;
if (!isMetadataSame(lhs_bis_config.codec_specific_params,
rhs_bis_config.codec_specific_params))
return false;
}
}
return true;
}
bool operator==(const PublicBroadcastAnnouncementData& lhs,
const PublicBroadcastAnnouncementData& rhs) {
if (lhs.features != rhs.features) return false;
if (!isMetadataSame(lhs.metadata, rhs.metadata)) return false;
return true;
}
} // namespace le_audio
} // namespace bluetooth