Skip to main content

dvb_si/collect/
bat.rs

1use crate::descriptors::DescriptorRegistry;
2use crate::tables::bat;
3use alloc::vec::Vec;
4
5use super::{CompleteSectionSet, ParsedDescriptorLoop};
6
7/// Transport-stream entry in a complete BAT.
8#[derive(Debug)]
9#[non_exhaustive]
10pub struct CompleteBatTransportStream<'a> {
11    /// transport_stream_id of the described TS.
12    pub transport_stream_id: u16,
13    /// original_network_id of the described TS.
14    pub original_network_id: u16,
15    /// Typed descriptor loop for this transport stream.
16    pub descriptors: ParsedDescriptorLoop<'a>,
17}
18
19/// Complete logical Bouquet Association Table.
20#[derive(Debug)]
21#[non_exhaustive]
22pub struct CompleteBat<'a> {
23    /// Bouquet identifier.
24    pub bouquet_id: u16,
25    /// 5-bit version_number.
26    pub version_number: u8,
27    /// current_next_indicator bit.
28    pub current_next_indicator: bool,
29    /// Bouquet descriptors, concatenated across every section that carries a
30    /// `bouquet_descriptors_loop` (EN 300 468 ยง5.2.2: each section has its
31    /// own loop, so a bouquet name/linkage split across sections is not
32    /// lost).
33    pub bouquet_descriptors: ParsedDescriptorLoop<'a>,
34    /// Transport-stream loop entries from all sections in wire order.
35    pub transport_streams: Vec<CompleteBatTransportStream<'a>>,
36}
37
38impl<'a> CompleteBat<'a> {
39    pub(crate) fn parse(
40        set: &'a CompleteSectionSet,
41        registry: Option<&'a DescriptorRegistry>,
42    ) -> crate::Result<Self> {
43        let sections: Vec<bat::BatSection<'a>> = set.parse_sections()?;
44        let first = sections.first().ok_or(crate::Error::BufferTooShort {
45            need: 1,
46            have: 0,
47            what: "CompleteBat sections",
48        })?;
49        let mut transport_streams = Vec::new();
50        for section in &sections {
51            transport_streams.extend(section.transport_streams.iter().map(|ts| {
52                CompleteBatTransportStream {
53                    transport_stream_id: ts.transport_stream_id,
54                    original_network_id: ts.original_network_id,
55                    descriptors: ParsedDescriptorLoop::parse(ts.descriptors, registry),
56                }
57            }));
58        }
59        Ok(Self {
60            bouquet_id: first.bouquet_id,
61            version_number: first.version_number,
62            current_next_indicator: first.current_next_indicator,
63            bouquet_descriptors: ParsedDescriptorLoop::parse_all(
64                sections.iter().map(|section| section.bouquet_descriptors),
65                registry,
66            ),
67            transport_streams,
68        })
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::super::SectionSetCollector;
75    use crate::descriptors::DescriptorLoop;
76    use crate::tables::bat::BatSection;
77
78    fn build_bat_section(
79        bouquet_id: u16,
80        version: u8,
81        section_number: u8,
82        last_section_number: u8,
83        bouquet_desc: &[u8],
84        ts_entries: &[(u16, u16, &[u8])],
85    ) -> Vec<u8> {
86        use broadcast_common::Serialize;
87        let bat = BatSection::new(
88            bouquet_id,
89            version,
90            true,
91            section_number,
92            last_section_number,
93            DescriptorLoop::new(bouquet_desc),
94            ts_entries
95                .iter()
96                .map(|(tsid, onid, desc)| {
97                    crate::tables::bat::BatTransportStream::new(
98                        *tsid,
99                        *onid,
100                        DescriptorLoop::new(desc),
101                    )
102                })
103                .collect(),
104        );
105        let mut buf = vec![0u8; bat.serialized_len()];
106        bat.serialize_into(&mut buf).unwrap();
107        buf
108    }
109
110    #[test]
111    fn complete_bat_preserves_bouquet_descriptors_and_ts_entries() {
112        let bouquet_name = [0x47u8, 0x03, b'F', b'o', b'o'];
113        let ts_desc = [0x43u8, 0x01, 0x01];
114
115        let sec = build_bat_section(
116            0x1234,
117            3,
118            0,
119            0,
120            &bouquet_name,
121            &[(0x1001, 0x2002, &ts_desc)],
122        );
123
124        let mut collector = SectionSetCollector::new();
125        let set = collector.push_section(&sec).unwrap().unwrap();
126        let complete = set.bat().unwrap();
127
128        assert_eq!(complete.bouquet_id, 0x1234);
129        assert_eq!(complete.version_number, 3);
130        assert!(complete.current_next_indicator);
131        assert_eq!(complete.transport_streams.len(), 1);
132        assert_eq!(complete.transport_streams[0].transport_stream_id, 0x1001);
133        assert_eq!(complete.transport_streams[0].original_network_id, 0x2002);
134    }
135}