blob: 542d8811b2850b2092fec1fdfe85cbb419e449fb [file]
/*
* 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 <base/strings/string_number_conversions.h>
#include <vector>
#include "bta/le_audio/audio_hal_client/audio_hal_client.h"
#include "bta_le_audio_broadcaster_api.h"
#include "btm_ble_api_types.h"
#include "internal_include/stack_config.h"
#include "osi/include/properties.h"
#include "stack/include/bt_types.h"
using bluetooth::le_audio::BasicAudioAnnouncementBisConfig;
using bluetooth::le_audio::BasicAudioAnnouncementCodecConfig;
using bluetooth::le_audio::BasicAudioAnnouncementData;
using bluetooth::le_audio::BasicAudioAnnouncementSubgroup;
using bluetooth::le_audio::types::LeAudioContextType;
namespace bluetooth::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_us);
}
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 codec_spec_raw_sz = ltv.RawPacketSize();
if (config.vendor_codec_specific_params) {
codec_spec_raw_sz = config.vendor_codec_specific_params->size();
}
// Add 1 for the codec spec. config length + config spec. data itself
codec_config_length += 1 + codec_spec_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 (either vendor specific or the LTVs)
UINT8_TO_STREAM(p_value, codec_spec_raw_sz);
if (config.vendor_codec_specific_params) {
ARRAY_TO_STREAM(
p_value, config.vendor_codec_specific_params->data(),
static_cast<int>(config.vendor_codec_specific_params->size()));
} else {
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);
}
le_audio::LeAudioCodecConfiguration
BroadcastConfiguration::GetAudioHalClientConfig() const {
return {
// Get the maximum number of channels
.num_channels = GetNumChannelsMax(),
// Get the max sampling frequency
.sample_rate = GetSamplingFrequencyHzMax(),
// Use the default 16 bits per sample resolution in the audio framework
.bits_per_sample = 16,
// Get the data interval
.data_interval_us = GetSduIntervalUs(),
};
}
std::ostream& operator<<(
std::ostream& os,
const bluetooth::le_audio::broadcaster::BroadcastSubgroupCodecConfig&
config) {
os << " BroadcastSubgroupCodecConfig={";
os << "CodecID="
<< "{" << +config.GetLeAudioCodecId().coding_format << ":"
<< +config.GetLeAudioCodecId().vendor_company_id << ":"
<< +config.GetLeAudioCodecId().vendor_codec_id << "}, ";
os << "BISes=[";
if (!config.bis_codec_configs_.empty()) {
for (auto const& bis_config : config.bis_codec_configs_) {
os << bis_config << ", ";
}
os << "\b\b";
}
os << "]";
os << ", BitsPerSample=" << +config.GetBitsPerSample() << "}";
os << "}";
return os;
}
std::ostream& operator<<(
std::ostream& os,
const le_audio::broadcaster::BroadcastSubgroupBisCodecConfig& config) {
os << "BisCfg={numBis=" << +config.GetNumBis()
<< ", NumChannelsPerBis=" << +config.GetNumChannelsPerBis()
<< ", CodecSpecific=" << config.GetCodecSpecData().GetAsCoreCodecConfig();
if (config.GetVendorCodecSpecific().has_value()) {
os << ", VendorSpecific=[";
if (!config.GetVendorCodecSpecific()->empty()) {
os << base::HexEncode(config.GetVendorCodecSpecific()->data(),
config.GetVendorCodecSpecific()->size());
}
os << "]";
}
os << "}";
return os;
}
std::ostream& operator<<(
std::ostream& os,
const bluetooth::le_audio::broadcaster::BroadcastQosConfig& config) {
os << " BroadcastQosConfig=[";
os << "RTN=" << +config.getRetransmissionNumber();
os << ", MaxTransportLatency=" << config.getMaxTransportLatency();
os << "]";
return os;
}
std::ostream& operator<<(
std::ostream& os,
const le_audio::broadcaster::BroadcastConfiguration& config) {
os << "BroadcastCfg={";
for (const auto& subgroup_cfg : config.subgroups) {
os << subgroup_cfg << std::endl;
}
os << config.qos << std::endl;
os << config.data_path << std::endl;
os << ", sduIntervalUs=" << config.sduIntervalUs;
os << ", maxSduOctets=" << config.maxSduOctets;
os << ", phy=" << config.phy;
os << ", packing=" << config.packing;
os << ", framing=" << config.framing;
os << "}" << std::endl;
return os;
}
} /* namespace broadcaster */
} // namespace bluetooth::le_audio
/* Helper functions for comparing BroadcastAnnouncements */
namespace bluetooth::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_us != rhs.presentation_delay_us) 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 bluetooth::le_audio