blob: 8c6074e53b82908e216363c7a35e9feb683e0773 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use bt_bass::client::BigToBisSync;
use bt_bass::types::{BigSubgroup, BisSync, BroadcastId};
use bt_common::core::ltv::LtValue;
use bt_common::core::{Address, AddressType, CodecId};
use bt_common::core::{AdvertisingSetId, PaInterval};
use bt_common::generic_audio::codec_configuration::CodecConfiguration;
use bt_common::generic_audio::metadata_ltv::Metadata;
use bt_common::packet_encoding::{Decodable, Error as PacketError};
/// Broadcast source data as advertised through Basic Audio Announcement
/// PA and Broadcast Audio Announcement.
/// See BAP spec v1.0.1 Section 3.7.2.1 and Section 3.7.2.2 for details.
// TODO(b/308481381): fill out endpoint from basic audio announcement from PA trains.
#[derive(Clone, Default, Debug, PartialEq)]
pub struct BroadcastSource {
pub(crate) address: Option<Address>,
pub(crate) address_type: Option<AddressType>,
pub(crate) advertising_sid: Option<AdvertisingSetId>,
pub(crate) broadcast_id: Option<BroadcastId>,
pub(crate) pa_interval: Option<PaInterval>,
pub(crate) endpoint: Option<BroadcastAudioSourceEndpoint>,
}
impl BroadcastSource {
/// Returns whether or not this BroadcastSource has enough information
/// to be added by the Broadcast Assistant.
pub(crate) fn into_add_source(&self) -> bool {
// PA interval is not necessary since default value can be used.
self.address.is_some()
&& self.address_type.is_some()
&& self.advertising_sid.is_some()
&& self.broadcast_id.is_some()
&& self.endpoint.is_some()
}
pub fn with_address(&mut self, address: [u8; 6]) -> &mut Self {
self.address = Some(address);
self
}
pub fn with_address_type(&mut self, type_: AddressType) -> &mut Self {
self.address_type = Some(type_);
self
}
pub fn with_broadcast_id(&mut self, bid: BroadcastId) -> &mut Self {
self.broadcast_id = Some(bid);
self
}
pub fn with_advertising_sid(&mut self, sid: AdvertisingSetId) -> &mut Self {
self.advertising_sid = Some(sid);
self
}
pub fn with_endpoint(&mut self, endpoint: BroadcastAudioSourceEndpoint) -> &mut Self {
self.endpoint = Some(endpoint);
self
}
/// Merge fields from other broadcast source into this broadcast source.
/// Set fields in other source take priority over this source.
/// If a field in the other broadcast source is none, it's ignored and
/// the existing values are kept.
pub(crate) fn merge(&mut self, other: &BroadcastSource) {
if let Some(address) = other.address {
self.address = Some(address);
}
if let Some(address_type) = other.address_type {
self.address_type = Some(address_type);
}
if let Some(advertising_sid) = other.advertising_sid {
self.advertising_sid = Some(advertising_sid);
}
if let Some(broadcast_id) = other.broadcast_id {
self.broadcast_id = Some(broadcast_id);
}
if let Some(pa_interval) = other.pa_interval {
self.pa_interval = Some(pa_interval);
}
if let Some(endpoint) = &other.endpoint {
self.endpoint = Some(endpoint.clone());
}
}
}
/// Parameters exposed as part of Basic Audio Announcement from Broadcast
/// Sources. See BAP spec v1.0.1 Section 3.7.2.2 for more details.
// TODO(b/308481381): Fill out the struct.
#[derive(Clone, Debug, PartialEq)]
pub struct BroadcastAudioSourceEndpoint {
// Delay is 3 bytes.
pub(crate) presentation_delay_ms: u32,
pub(crate) big: Vec<BroadcastIsochronousGroup>,
}
impl BroadcastAudioSourceEndpoint {
// Should contain presentation delay, num BIG, and at least one BIG praram.
const MIN_PACKET_SIZE: usize = 3 + 1 + BroadcastIsochronousGroup::MIN_PACKET_SIZE;
/// Returns the representation of this object's broadcast isochronous groups
/// that's usable with Broadcast Audio Scan Service operations.
///
/// # Arguments
///
/// * `bis_sync` - BIG to BIS sync information. If the set is empty, no
/// preference value is used for all the BIGs
pub(crate) fn get_bass_subgroups(
&self,
bis_sync: BigToBisSync,
) -> Result<Vec<BigSubgroup>, PacketError> {
let mut subgroups = Vec::new();
let sync_map = bt_bass::client::big_to_bis_sync_indices(&bis_sync);
for (big_index, group) in self.big.iter().enumerate() {
let bis_sync = match sync_map.get(&(big_index as u8)) {
Some(bis_indices) => {
let mut bis_sync: BisSync = BisSync(0);
bis_sync.set_sync(bis_indices)?;
bis_sync
}
_ => BisSync::default(),
};
subgroups.push(BigSubgroup::new(Some(bis_sync)).with_metadata(group.metadata.clone()));
}
Ok(subgroups)
}
}
impl Decodable for BroadcastAudioSourceEndpoint {
type Error = PacketError;
fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
if buf.len() < Self::MIN_PACKET_SIZE {
return Err(PacketError::UnexpectedDataLength);
}
let mut idx = 0 as usize;
let presentation_delay = u32::from_le_bytes([buf[idx], buf[idx + 1], buf[idx + 2], 0x00]);
idx += 3;
let num_big: usize = buf[idx] as usize;
idx += 1;
if num_big < 1 {
return Err(PacketError::InvalidParameter(format!(
"num of subgroups shall be at least 1 got {num_big}"
)));
}
let mut big = Vec::new();
while big.len() < num_big {
let (group, len) = BroadcastIsochronousGroup::decode(&buf[idx..])
.map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
big.push(group);
idx += len;
}
Ok((Self { presentation_delay_ms: presentation_delay, big }, idx))
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct BroadcastIsochronousGroup {
pub(crate) codec_id: CodecId,
pub(crate) codec_specific_config: Vec<CodecConfiguration>,
pub(crate) metadata: Vec<Metadata>,
pub(crate) bis: Vec<BroadcastIsochronousStream>,
}
impl BroadcastIsochronousGroup {
// Should contain num BIS, codec id, codec specific config len, metadata len,
// and at least one BIS praram.
const MIN_PACKET_SIZE: usize =
1 + CodecId::BYTE_SIZE + 1 + 1 + BroadcastIsochronousStream::MIN_PACKET_SIZE;
}
impl Decodable for BroadcastIsochronousGroup {
type Error = PacketError;
fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
if buf.len() < BroadcastIsochronousGroup::MIN_PACKET_SIZE {
return Err(PacketError::UnexpectedDataLength);
}
let mut idx = 0;
let num_bis = buf[idx] as usize;
idx += 1;
if num_bis < 1 {
return Err(PacketError::InvalidParameter(format!(
"num of BIS shall be at least 1 got {num_bis}"
)));
}
let (codec_id, read_bytes) = CodecId::decode(&buf[idx..])?;
idx += read_bytes;
let codec_config_len = buf[idx] as usize;
idx += 1;
let (results, consumed) = CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]);
if consumed != codec_config_len {
return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
}
let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect();
idx += codec_config_len;
let metadata_len = buf[idx] as usize;
idx += 1;
let (results_metadata, consumed_len) = Metadata::decode_all(&buf[idx..idx + metadata_len]);
if consumed_len != metadata_len {
return Err(PacketError::UnexpectedDataLength);
}
// Ignore any undecodable metadata types
let metadata = results_metadata.into_iter().filter_map(Result::ok).collect();
idx += consumed_len;
let mut bis = Vec::new();
while bis.len() < num_bis {
let (stream, len) = BroadcastIsochronousStream::decode(&buf[idx..])
.map_err(|e| PacketError::InvalidParameter(e.to_string()))?;
bis.push(stream);
idx += len;
}
Ok((
BroadcastIsochronousGroup {
codec_id,
codec_specific_config: codec_specific_configs,
metadata,
bis,
},
idx,
))
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct BroadcastIsochronousStream {
pub(crate) bis_index: u8,
pub(crate) codec_specific_config: Vec<CodecConfiguration>,
}
impl BroadcastIsochronousStream {
const MIN_PACKET_SIZE: usize = 1 + 1;
}
impl Decodable for BroadcastIsochronousStream {
type Error = PacketError;
fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
if buf.len() < BroadcastIsochronousStream::MIN_PACKET_SIZE {
return Err(PacketError::UnexpectedDataLength);
}
let mut idx = 0;
let bis_index = buf[idx];
idx += 1;
let codec_config_len = buf[idx] as usize;
idx += 1;
let (results, consumed) = CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]);
if consumed != codec_config_len {
return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
}
let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect();
idx += codec_config_len;
Ok((
BroadcastIsochronousStream { bis_index, codec_specific_config: codec_specific_configs },
idx,
))
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
use bt_common::generic_audio::codec_configuration::{FrameDuration, SamplingFrequency};
use bt_common::generic_audio::AudioLocation;
#[test]
fn decode_bis() {
#[rustfmt::skip]
let buf = [
0x01, 0x09, // bis index and codec specific config len
0x02, 0x01, 0x06, // sampling frequency LTV
0x05, 0x03, 0x03, 0x00, 0x00, 0x0C, // audio location LTV
];
let (bis, _read_bytes) =
BroadcastIsochronousStream::decode(&buf[..]).expect("should not fail");
assert_eq!(
bis,
BroadcastIsochronousStream {
bis_index: 0x01,
codec_specific_config: vec![
CodecConfiguration::SamplingFrequency(SamplingFrequency::F32000Hz),
CodecConfiguration::AudioChannelAllocation(HashSet::from([
AudioLocation::FrontLeft,
AudioLocation::FrontRight,
AudioLocation::LeftSurround,
AudioLocation::RightSurround
])),
],
}
);
}
#[test]
fn decode_big() {
#[rustfmt::skip]
let buf = [
0x02, 0x03, 0x00, 0x00, 0x00, 0x00, // num of bis, codec id
0x04, 0x03, 0x04, 0x04, 0x10, // codec specific config len, octets per codec frame LTV
0x03, 0x02, 0x08, 0x01, // metadata len, audio active state LTV
0x01, 0x00, // bis index, codec specific config len (bis #1)
0x02, 0x03, 0x02, 0x02, 0x01, // bis index, codec specific config len, frame duration LTV (bis #2)
];
let (big, _read_bytes) =
BroadcastIsochronousGroup::decode(&buf[..]).expect("should not fail");
assert_eq!(
big,
BroadcastIsochronousGroup {
codec_id: CodecId::Assigned(bt_common::core::CodingFormat::Transparent),
codec_specific_config: vec![CodecConfiguration::OctetsPerCodecFrame(0x1004),],
metadata: vec![Metadata::AudioActiveState(true)],
bis: vec![
BroadcastIsochronousStream { bis_index: 0x01, codec_specific_config: vec![] },
BroadcastIsochronousStream {
bis_index: 0x02,
codec_specific_config: vec![CodecConfiguration::FrameDuration(
FrameDuration::TenMs
)],
},
],
}
);
}
#[test]
fn decode_base() {
#[rustfmt::skip]
let buf = [
0x10, 0x20, 0x30, 0x02, // presentation delay, num of subgroups
0x01, 0x03, 0x00, 0x00, 0x00, 0x00, // num of bis, codec id (big #1)
0x00, // codec specific config len
0x00, // metadata len,
0x01, 0x00, // bis index, codec specific config len (big #1 / bis #1)
0x01, 0x02, 0x00, 0x00, 0x00, 0x00, // num of bis, codec id (big #2)
0x00, // codec specific config len
0x00, // metadata len,
0x01, 0x03, 0x02, 0x05, 0x08, // bis index, codec specific config len, codec frame blocks LTV (big #2 / bis #2)
];
let (base, _read_bytes) =
BroadcastAudioSourceEndpoint::decode(&buf[..]).expect("should not fail");
assert_eq!(base.presentation_delay_ms, 0x00302010);
assert_eq!(base.big.len(), 2);
assert_eq!(
base.big[0],
BroadcastIsochronousGroup {
codec_id: CodecId::Assigned(bt_common::core::CodingFormat::Transparent),
codec_specific_config: vec![],
metadata: vec![],
bis: vec![BroadcastIsochronousStream {
bis_index: 0x01,
codec_specific_config: vec![],
},],
}
);
assert_eq!(
base.big[1],
BroadcastIsochronousGroup {
codec_id: CodecId::Assigned(bt_common::core::CodingFormat::Cvsd),
codec_specific_config: vec![],
metadata: vec![],
bis: vec![BroadcastIsochronousStream {
bis_index: 0x01,
codec_specific_config: vec![CodecConfiguration::CodecFramesPerSdu(0x08)],
},],
}
);
}
#[test]
fn decode_base_complex() {
// BroadcastAudioSourceEndpoint { presentation_delay_ms: 20000, big:
// [BroadcastIsochronousGroup { codec_id: Assigned(Lc3), codec_specific_config:
// [SamplingFrequency(F24000Hz), FrameDuration(TenMs),
// AudioChannelAllocation({FrontLeft, FrontRight}), OctetsPerCodecFrame(60)],
// metadata: [StreamingAudioContexts([Media])], bis:
// [BroadcastIsochronousStream { bis_index: 1, codec_specific_config:
// [AudioChannelAllocation({FrontLeft})] }, BroadcastIsochronousStream {
// bis_index: 2, codec_specific_con$
// ig: [AudioChannelAllocation({FrontRight})] }] }] }
#[rustfmt::skip]
let buf = [
0x20, 0x4e, 0x00, 0x01, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05,
0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x04, 0x3c, 0x00, 0x04, 0x03, 0x02, 0x04, 0x00, 0x01, 0x06, 0x05, 0x03, 0x01,
0x00, 0x00, 0x00, 0x02, 0x06, 0x05, 0x03, 0x02, 0x00, 0x00, 0x00
];
let (base, _read_bytes) =
BroadcastAudioSourceEndpoint::decode(&buf[..]).expect("should not fail");
println!("{base:?}");
// BroadcastAudioSourceEndpoint { presentation_delay_ms: 20000, big:
// [BroadcastIsochronousGroup { codec_id: Assigned(Lc3), codec_specific_config:
// [SamplingFrequency(F24000Hz), FrameDuration(TenMs),
// AudioChannelAllocation({FrontLeft}), OctetsPerCodecFrame(60)],
// metadata: [BroadcastAudioImmediateRenderingFlag], bis:
// [BroadcastIsochronousStream { bis_index: 1, codec_specific_config:
// [SamplingFrequency(F24000Hz)] }] }] }
#[rustfmt::skip]
let buf = [
0x20, 0x4e, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x05,
0x02, 0x02, 0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x04, 0x3c, 0x00, 0x02,
0x01, 0x09, 0x01, 0x03, 0x02, 0x01, 0x05,
];
let (base, _read_bytes) =
BroadcastAudioSourceEndpoint::decode(&buf[..]).expect("should not fail");
println!("{base:?}");
}
}