Skip to main content

bt_bap/
types.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use bt_common::core::ltv::LtValue;
6use bt_common::core::CodecId;
7use bt_common::generic_audio::codec_configuration::CodecConfiguration;
8use bt_common::generic_audio::metadata_ltv::Metadata;
9use bt_common::packet_encoding::{Decodable, Encodable, Error as PacketError};
10
11/// Broadcast_ID is a 3-byte data on the wire.
12/// Defined in BAP spec v1.0.1 section 3.7.2.1.
13#[derive(Clone, Copy, Eq, Hash, PartialEq)]
14pub struct BroadcastId(u32);
15
16impl BroadcastId {
17    // On the wire, Broadcast_ID is transported in 3 bytes.
18    pub const BYTE_SIZE: usize = 3;
19
20    pub fn new(raw_value: u32) -> Self {
21        Self(raw_value)
22    }
23}
24
25impl std::fmt::Display for BroadcastId {
26    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
27        write!(f, "{:08x}", self.0)
28    }
29}
30
31impl std::fmt::Debug for BroadcastId {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        f.debug_tuple("BroadcastId").field(&format_args!("0x{}", self)).finish()
34    }
35}
36
37impl From<BroadcastId> for u32 {
38    fn from(value: BroadcastId) -> u32 {
39        value.0
40    }
41}
42
43impl TryFrom<u32> for BroadcastId {
44    type Error = PacketError;
45
46    fn try_from(value: u32) -> Result<Self, Self::Error> {
47        const MAX_VALUE: u32 = 0xFFFFFF;
48        if value > MAX_VALUE {
49            return Err(PacketError::InvalidParameter(format!(
50                "Broadcast ID cannot exceed 3 bytes"
51            )));
52        }
53        Ok(BroadcastId(value))
54    }
55}
56
57impl Decodable for BroadcastId {
58    type Error = PacketError;
59
60    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
61        if buf.len() != Self::BYTE_SIZE {
62            return (Err(PacketError::UnexpectedDataLength), buf.len());
63        }
64
65        let padded_bytes = [buf[0], buf[1], buf[2], 0x00];
66        (Ok(BroadcastId(u32::from_le_bytes(padded_bytes))), Self::BYTE_SIZE)
67    }
68}
69
70impl Encodable for BroadcastId {
71    type Error = PacketError;
72
73    fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
74        if buf.len() < self.encoded_len() {
75            return Err(PacketError::BufferTooSmall);
76        }
77        // Since 3-byte value is being fit into u32, we ignore the most significant
78        // byte.
79        buf[0..3].copy_from_slice(&self.0.to_le_bytes()[0..3]);
80        Ok(())
81    }
82
83    fn encoded_len(&self) -> core::primitive::usize {
84        Self::BYTE_SIZE
85    }
86}
87
88/// To associate a PA, used to expose broadcast Audio Stream parameters, with a
89/// broadcast Audio Stream, the Broadcast Source shall transmit EA PDUs that
90/// include the following data. This struct represents the AD data value
91/// excluding the 2-octet Service UUID. See BAP v1.0.1 Section 3.7.2.1 for more
92/// details.
93#[derive(Clone, Copy, Debug, PartialEq)]
94pub struct BroadcastAudioAnnouncement {
95    pub broadcast_id: BroadcastId,
96}
97
98impl BroadcastAudioAnnouncement {
99    const PACKET_SIZE: usize = BroadcastId::BYTE_SIZE;
100}
101
102impl Decodable for BroadcastAudioAnnouncement {
103    type Error = PacketError;
104
105    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
106        if buf.len() < Self::PACKET_SIZE {
107            return (Err(PacketError::UnexpectedDataLength), buf.len());
108        }
109
110        match BroadcastId::decode(&buf[0..3]) {
111            (Ok(broadcast_id), _) => {
112                // According to the spec, broadcast audio announcement service data inlcudes
113                // broadcast id and any additional service data. We don't store any
114                // additional parameters, so for now we just "consume" all of data buffer
115                // without doing anything.
116                (Ok(Self { broadcast_id }), buf.len())
117            }
118            (Err(e), _) => (Err(e), buf.len()),
119        }
120    }
121}
122
123/// Parameters exposed as part of Basic Audio Announcement from Broadcast
124/// Sources. See BAP v1.0.1 Section 3.7.2.2 for more details.
125// TODO(b/308481381): Fill out the struct.
126#[derive(Clone, Debug, PartialEq)]
127pub struct BroadcastAudioSourceEndpoint {
128    // Actual value is 3 bytes long.
129    pub presentation_delay_ms: u32,
130    pub big: Vec<BroadcastIsochronousGroup>,
131}
132
133impl BroadcastAudioSourceEndpoint {
134    // Should contain presentation delay, num BIG, and at least one BIG praram.
135    const MIN_PACKET_SIZE: usize = 3 + 1 + BroadcastIsochronousGroup::MIN_PACKET_SIZE;
136}
137
138impl Decodable for BroadcastAudioSourceEndpoint {
139    type Error = PacketError;
140
141    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
142        if buf.len() < Self::MIN_PACKET_SIZE {
143            return (Err(PacketError::UnexpectedDataLength), buf.len());
144        }
145
146        let mut idx = 0 as usize;
147        let presentation_delay = u32::from_le_bytes([buf[idx], buf[idx + 1], buf[idx + 2], 0x00]);
148        idx += 3;
149
150        let num_big: usize = buf[idx] as usize;
151        idx += 1;
152        if num_big < 1 {
153            return (
154                Err(PacketError::InvalidParameter(format!(
155                    "num of subgroups shall be at least 1 got {num_big}"
156                ))),
157                buf.len(),
158            );
159        }
160        let mut big = Vec::new();
161        while big.len() < num_big {
162            match BroadcastIsochronousGroup::decode(&buf[idx..]) {
163                (Ok(group), consumed) => {
164                    big.push(group);
165                    idx += consumed;
166                }
167                (Err(e), _) => {
168                    return (Err(PacketError::InvalidParameter(e.to_string())), buf.len());
169                }
170            };
171        }
172
173        (Ok(Self { presentation_delay_ms: presentation_delay, big }), idx)
174    }
175}
176
177/// A single subgroup in a Basic Audio Announcement as outlined in
178/// BAP spec v1.0.1 Section 3.7.2.2. Each subgroup is used to
179/// group BISes present in the broadcast isochronous group.
180#[derive(Clone, Debug, PartialEq)]
181pub struct BroadcastIsochronousGroup {
182    pub codec_id: CodecId,
183    pub codec_specific_configs: Vec<CodecConfiguration>,
184    pub metadata: Vec<Metadata>,
185    pub bis: Vec<BroadcastIsochronousStream>,
186}
187
188impl BroadcastIsochronousGroup {
189    // Should contain num BIS, codec id, codec specific config len, metadata len,
190    // and at least one BIS praram.
191    const MIN_PACKET_SIZE: usize =
192        1 + CodecId::BYTE_SIZE + 1 + 1 + BroadcastIsochronousStream::MIN_PACKET_SIZE;
193}
194
195impl Decodable for BroadcastIsochronousGroup {
196    type Error = PacketError;
197
198    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
199        if buf.len() < BroadcastIsochronousGroup::MIN_PACKET_SIZE {
200            return (Err(PacketError::UnexpectedDataLength), buf.len());
201        }
202
203        let mut idx = 0;
204        let num_bis = buf[idx] as usize;
205        idx += 1;
206        if num_bis < 1 {
207            return (
208                Err(PacketError::InvalidParameter(format!(
209                    "num of BIS shall be at least 1 got {num_bis}"
210                ))),
211                buf.len(),
212            );
213        }
214
215        let mut decode_fn = || {
216            let codec_id;
217            match CodecId::decode(&buf[idx..]) {
218                (Ok(id), consumed) => {
219                    codec_id = id;
220                    idx += consumed;
221                }
222                (Err(e), _) => {
223                    return Err(e);
224                }
225            };
226
227            let codec_config_len = buf[idx] as usize;
228            idx += 1;
229            if idx + codec_config_len > buf.len() {
230                return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
231            }
232            let (results, consumed) =
233                CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]);
234            if consumed != codec_config_len {
235                return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
236            }
237
238            let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect();
239            idx += codec_config_len;
240
241            let metadata_len = buf[idx] as usize;
242            idx += 1;
243            if idx + metadata_len > buf.len() {
244                return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
245            }
246
247            let (results_metadata, consumed_len) =
248                Metadata::decode_all(&buf[idx..idx + metadata_len]);
249            if consumed_len != metadata_len {
250                return Err(PacketError::UnexpectedDataLength);
251            }
252            // Ignore any undecodable metadata types
253            let metadata = results_metadata.into_iter().filter_map(Result::ok).collect();
254            idx += consumed_len;
255
256            let mut bis = Vec::new();
257            while bis.len() < num_bis {
258                match BroadcastIsochronousStream::decode(&buf[idx..]) {
259                    (Ok(stream), consumed) => {
260                        bis.push(stream);
261                        idx += consumed;
262                    }
263                    (Err(e), _consumed) => {
264                        return Err(PacketError::InvalidParameter(e.to_string()));
265                    }
266                }
267            }
268
269            Ok((BroadcastIsochronousGroup { codec_id, codec_specific_configs, metadata, bis }, idx))
270        };
271        match decode_fn() {
272            Ok((obj, consumed)) => (Ok(obj), consumed),
273            Err(e) => (Err(e), buf.len()),
274        }
275    }
276}
277
278#[derive(Clone, Debug, PartialEq)]
279pub struct BroadcastIsochronousStream {
280    pub bis_index: u8,
281    pub codec_specific_config: Vec<CodecConfiguration>,
282}
283
284impl BroadcastIsochronousStream {
285    const MIN_PACKET_SIZE: usize = 1 + 1;
286}
287
288impl Decodable for BroadcastIsochronousStream {
289    type Error = PacketError;
290
291    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
292        if buf.len() < BroadcastIsochronousStream::MIN_PACKET_SIZE {
293            return (Err(PacketError::UnexpectedDataLength), buf.len());
294        }
295
296        let mut idx = 0;
297
298        let bis_index = buf[idx];
299        idx += 1;
300
301        let codec_config_len = buf[idx] as usize;
302        idx += 1;
303
304        let (results, consumed) = CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]);
305        if consumed != codec_config_len {
306            return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len());
307        }
308        let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect();
309        idx += codec_config_len;
310
311        (
312            Ok(BroadcastIsochronousStream {
313                bis_index,
314                codec_specific_config: codec_specific_configs,
315            }),
316            idx,
317        )
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324
325    use std::collections::HashSet;
326
327    use bt_common::generic_audio::codec_configuration::{FrameDuration, SamplingFrequency};
328    use bt_common::generic_audio::AudioLocation;
329
330    #[test]
331    fn broadcast_id() {
332        // Value bigger than 3 bytes is not a valid broadcast ID.
333        let _ = BroadcastId::try_from(0x010A0B0C).expect_err("should fail");
334
335        let id = BroadcastId::try_from(0x000A0B0C).expect("should succeed");
336
337        assert_eq!(id.encoded_len(), 3);
338        let mut buf = vec![0; id.encoded_len()];
339        let _ = id.encode(&mut buf[..]).expect("should have succeeded");
340
341        let bytes = vec![0x0C, 0x0B, 0x0A];
342        assert_eq!(buf, bytes);
343
344        let (got, bytes) = BroadcastId::decode(&bytes);
345        assert_eq!(got, Ok(id));
346        assert_eq!(bytes, BroadcastId::BYTE_SIZE);
347        let got = BroadcastId::try_from(u32::from_le_bytes([0x0C, 0x0B, 0x0A, 0x00]))
348            .expect("should succeed");
349        assert_eq!(got, id);
350    }
351
352    #[test]
353    fn broadcast_audio_announcement() {
354        let bytes = vec![0x0C, 0x0B, 0x0A];
355        let broadcast_id = BroadcastId::try_from(0x000A0B0C).unwrap();
356
357        let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes);
358        assert_eq!(got, Ok(BroadcastAudioAnnouncement { broadcast_id }));
359        assert_eq!(consumed, 3);
360
361        let bytes = vec![
362            0x0C, 0x0B, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, /* some other additional data */
363        ];
364        let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes);
365        assert_eq!(got, Ok(BroadcastAudioAnnouncement { broadcast_id }));
366        assert_eq!(consumed, 8);
367    }
368
369    #[test]
370    fn decode_bis() {
371        #[rustfmt::skip]
372        let buf = [
373            0x01, 0x09,                          // bis index and codec specific config len
374            0x02, 0x01, 0x06,                    // sampling frequency LTV
375            0x05, 0x03, 0x03, 0x00, 0x00, 0x0C,  // audio location LTV
376        ];
377
378        let (bis, _read_bytes) = BroadcastIsochronousStream::decode(&buf[..]);
379        assert_eq!(
380            bis,
381            Ok(BroadcastIsochronousStream {
382                bis_index: 0x01,
383                codec_specific_config: vec![
384                    CodecConfiguration::SamplingFrequency(SamplingFrequency::F32000Hz),
385                    CodecConfiguration::AudioChannelAllocation(HashSet::from([
386                        AudioLocation::FrontLeft,
387                        AudioLocation::FrontRight,
388                        AudioLocation::LeftSurround,
389                        AudioLocation::RightSurround
390                    ])),
391                ],
392            })
393        );
394    }
395
396    #[test]
397    fn decode_big() {
398        #[rustfmt::skip]
399        let buf = [
400            0x02,  0x03, 0x00, 0x00, 0x00, 0x00,  // num of bis, codec id
401            0x04, 0x03, 0x04, 0x04, 0x10,         // codec specific config len, octets per codec frame LTV
402            0x03, 0x02, 0x08, 0x01,               // metadata len, audio active state LTV
403            0x01, 0x00,                           // bis index, codec specific config len (bis #1)
404            0x02, 0x03, 0x02, 0x02, 0x01,         // bis index, codec specific config len, frame duration LTV (bis #2)
405        ];
406
407        let big = BroadcastIsochronousGroup::decode(&buf[..]).0.expect("should not fail");
408        assert_eq!(
409            big,
410            BroadcastIsochronousGroup {
411                codec_id: CodecId::Assigned(bt_common::core::CodingFormat::Transparent),
412                codec_specific_configs: vec![CodecConfiguration::OctetsPerCodecFrame(0x1004),],
413                metadata: vec![Metadata::AudioActiveState(true)],
414                bis: vec![
415                    BroadcastIsochronousStream { bis_index: 0x01, codec_specific_config: vec![] },
416                    BroadcastIsochronousStream {
417                        bis_index: 0x02,
418                        codec_specific_config: vec![CodecConfiguration::FrameDuration(
419                            FrameDuration::TenMs
420                        )],
421                    },
422                ],
423            }
424        );
425    }
426
427    #[test]
428    fn decode_base() {
429        #[rustfmt::skip]
430        let buf = [
431            0x10, 0x20, 0x30, 0x02,               // presentation delay, num of subgroups
432            0x01, 0x03, 0x00, 0x00, 0x00, 0x00,   // num of bis, codec id (big #1)
433            0x00,                                 // codec specific config len
434            0x00,                                 // metadata len,
435            0x01, 0x00,                           // bis index, codec specific config len (big #1 / bis #1)
436            0x01, 0x02, 0x00, 0x00, 0x00, 0x00,   // num of bis, codec id (big #2)
437            0x00,                                 // codec specific config len
438            0x00,                                 // metadata len,
439            0x01, 0x03, 0x02, 0x05, 0x08,         // bis index, codec specific config len, codec frame blocks LTV (big #2 / bis #2)
440        ];
441
442        let base = BroadcastAudioSourceEndpoint::decode(&buf[..]).0.expect("should not fail");
443        assert_eq!(base.presentation_delay_ms, 0x00302010);
444        assert_eq!(base.big.len(), 2);
445        assert_eq!(
446            base.big[0],
447            BroadcastIsochronousGroup {
448                codec_id: CodecId::Assigned(bt_common::core::CodingFormat::Transparent),
449                codec_specific_configs: vec![],
450                metadata: vec![],
451                bis: vec![BroadcastIsochronousStream {
452                    bis_index: 0x01,
453                    codec_specific_config: vec![],
454                },],
455            }
456        );
457        assert_eq!(
458            base.big[1],
459            BroadcastIsochronousGroup {
460                codec_id: CodecId::Assigned(bt_common::core::CodingFormat::Cvsd),
461                codec_specific_configs: vec![],
462                metadata: vec![],
463                bis: vec![BroadcastIsochronousStream {
464                    bis_index: 0x01,
465                    codec_specific_config: vec![CodecConfiguration::CodecFramesPerSdu(0x08)],
466                },],
467            }
468        );
469    }
470
471    #[test]
472    fn decode_base_complex() {
473        #[rustfmt::skip]
474        let buf = [
475            0x20, 0x4e, 0x00,  // presentation_delay_ms: 20000 (little-endian)
476            0x01,  // # of subgroups
477            0x02,  // # of BIS in group 1
478            0x06, 0x00, 0x00, 0x00, 0x00,  // Codec ID (Lc3)
479            0x10,  // codec_specific_configuration_length of group 1
480            0x02, 0x01, 0x05,  // sampling frequency 24 kHz
481            0x02, 0x02, 0x01,  // 10 ms frame duration
482            0x05, 0x03, 0x03, 0x00, 0x00, 0x00,  // front left audio channel
483            0x03, 0x04, 0x3c, 0x00,  // 60 octets per codec frame
484            0x04,  // metadata_length of group 1
485            0x03, 0x02, 0x04, 0x00,  // media streaming audio context
486            0x01,  // BIS index of the 1st BIS in group 1
487            0x06,  // codec_specific_configuration_length of the 1st BIS
488            0x05, 0x03, 0x01, 0x00, 0x00, 0x00,  // front left audio channel
489            0x02,  // BIS index of the 2nd BIS in group 1
490            0x06,  // codec_specific_configuration_length of the 2nd BIS
491            0x05, 0x03, 0x02, 0x00, 0x00, 0x00,  // front right audio channel
492        ];
493        let (base, read_bytes) = BroadcastAudioSourceEndpoint::decode(&buf[..]);
494        let base = base.expect("should not fail");
495        assert_eq!(read_bytes, buf.len());
496        assert_eq!(base.presentation_delay_ms, 20000);
497        assert_eq!(base.big.len(), 1);
498        assert_eq!(base.big[0].bis.len(), 2);
499
500        #[rustfmt::skip]
501        let buf = [
502            0x20, 0x4e, 0x00,  // presentation_delay_ms: 20000 (little-endian)
503            0x01,  // # of subgroups
504            0x01,  // # of BIS in group 1
505            0x06, 0x00, 0x00, 0x00, 0x00,  // Codec ID (Lc3)
506            0x10,  // codec_specific_configuration_length of group 1
507            0x02, 0x01, 0x05,  // sampling frequency 24 kHz
508            0x02, 0x02, 0x01,  // 10 ms frame duration
509            0x05, 0x03, 0x01, 0x00, 0x00, 0x00,  // front left audio channel
510            0x03, 0x04, 0x3c, 0x00,  // 60 octets per codec frame
511            0x02,  // metadata_length of group 1
512            0x01, 0x09,  // broadcast audio immediate rendering flag
513            0x01,  // BIS index of the 1st BIS in group 1
514            0x03,  // codec_specific_configuration_length of the 1st BIS
515            0x02, 0x01, 0x05,  // sampling frequency 24 kHz
516        ];
517        let (base, read_bytes) = BroadcastAudioSourceEndpoint::decode(&buf[..]);
518        let base = base.expect("should not fail");
519        assert_eq!(read_bytes, buf.len());
520        assert_eq!(base.presentation_delay_ms, 20000);
521        assert_eq!(base.big.len(), 1);
522        assert_eq!(base.big[0].bis.len(), 1);
523    }
524}