mpeg2ts_reader/psi/
pmt.rs

1//! Types related to the _Program Map Table_
2
3use crate::demultiplex::DemuxError;
4use crate::descriptor;
5use crate::packet;
6use crate::StreamType;
7use log::warn;
8use std::fmt;
9
10/// Sections of the _Program Map Table_ give details of the streams within a particular program
11pub struct PmtSection<'buf> {
12    data: &'buf [u8],
13}
14impl<'buf> fmt::Debug for PmtSection<'buf> {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
16        f.debug_struct("PmtSection")
17            .field("pcr_pid", &self.pcr_pid())
18            .field("descriptors", &DescriptorsDebug(self))
19            .field("streams", &StreamsDebug(self))
20            .finish()
21    }
22}
23struct StreamsDebug<'buf>(&'buf PmtSection<'buf>);
24impl<'buf> fmt::Debug for StreamsDebug<'buf> {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
26        f.debug_list().entries(self.0.streams()).finish()
27    }
28}
29struct DescriptorsDebug<'buf>(&'buf PmtSection<'buf>);
30impl<'buf> fmt::Debug for DescriptorsDebug<'buf> {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
32        f.debug_list()
33            .entries(self.0.descriptors::<descriptor::CoreDescriptors<'buf>>())
34            .finish()
35    }
36}
37
38impl<'buf> PmtSection<'buf> {
39    /// Create a `PmtSection`, wrapping the given slice, whose methods can parse the section's
40    /// fields
41    pub fn from_bytes(data: &'buf [u8]) -> Result<PmtSection<'buf>, DemuxError> {
42        if data.len() < Self::HEADER_SIZE {
43            Err(DemuxError::NotEnoughData {
44                field: "program_map_section",
45                expected: Self::HEADER_SIZE,
46                actual: data.len(),
47            })
48        } else {
49            let sect = PmtSection { data };
50            let expected = sect.program_info_length() as usize + Self::HEADER_SIZE;
51            if data.len() < expected {
52                Err(DemuxError::NotEnoughData {
53                    field: "descriptor",
54                    expected,
55                    actual: data.len(),
56                })
57            } else {
58                Ok(sect)
59            }
60        }
61    }
62
63    /// Borrow a reference to the buffer containing the underlying PMT section data
64    pub fn buffer(&self) -> &[u8] {
65        self.data
66    }
67
68    const HEADER_SIZE: usize = 4;
69
70    /// Returns the Pid of packets that will contain the Program Clock Reference for this program
71    pub fn pcr_pid(&self) -> packet::Pid {
72        packet::Pid::new(u16::from(self.data[0] & 0b0001_1111) << 8 | u16::from(self.data[1]))
73    }
74    fn program_info_length(&self) -> u16 {
75        u16::from(self.data[2] & 0b0000_1111) << 8 | u16::from(self.data[3])
76    }
77    /// Returns an iterator over the descriptors attached to this PMT section.
78    pub fn descriptors<Desc: descriptor::Descriptor<'buf> + 'buf>(
79        &self,
80    ) -> impl Iterator<Item = Result<Desc, descriptor::DescriptorError>> + 'buf {
81        let descriptor_end = Self::HEADER_SIZE + self.program_info_length() as usize;
82        let descriptor_data = &self.data[Self::HEADER_SIZE..descriptor_end];
83        descriptor::DescriptorIter::new(descriptor_data)
84    }
85    /// Returns an iterator over the streams of which this program is composed
86    pub fn streams(&self) -> impl Iterator<Item = StreamInfo<'buf>> {
87        let descriptor_end = Self::HEADER_SIZE + self.program_info_length() as usize;
88        if descriptor_end > self.data.len() {
89            warn!(
90                "program_info_length={} extends beyond end of PMT section (section_length={})",
91                self.program_info_length(),
92                self.data.len()
93            );
94            // return an iterator that will produce no items,
95            StreamInfoIter::new(&self.data[0..0])
96        } else {
97            StreamInfoIter::new(&self.data[descriptor_end..])
98        }
99    }
100}
101/// Iterator over the `StreamInfo` entries in a `PmtSection`.
102struct StreamInfoIter<'buf> {
103    buf: &'buf [u8],
104}
105impl<'buf> StreamInfoIter<'buf> {
106    fn new(buf: &'buf [u8]) -> StreamInfoIter<'buf> {
107        StreamInfoIter { buf }
108    }
109}
110impl<'buf> Iterator for StreamInfoIter<'buf> {
111    type Item = StreamInfo<'buf>;
112
113    fn next(&mut self) -> Option<Self::Item> {
114        if self.buf.is_empty() {
115            return None;
116        }
117        if let Some((stream_info, info_len)) = StreamInfo::from_bytes(self.buf) {
118            self.buf = &self.buf[info_len..];
119            Some(stream_info)
120        } else {
121            None
122        }
123    }
124}
125
126/// Details of a particular elementary stream within a program.
127///
128///  - `stream_type` gives an indication of the kind of content carried within the stream
129///  - The `elementry_pid` property allows us to find Transport Stream packets that belong to the
130///    elementry stream
131///  - `descriptors` _may_ provide extra metadata describing some of the
132///     stream's properties (for example, the streams 'language' might be given in a descriptor; or
133///     it might not)
134pub struct StreamInfo<'buf> {
135    data: &'buf [u8],
136}
137
138impl<'buf> StreamInfo<'buf> {
139    const HEADER_SIZE: usize = 5;
140
141    fn from_bytes(data: &'buf [u8]) -> Option<(StreamInfo<'buf>, usize)> {
142        if data.len() < Self::HEADER_SIZE {
143            warn!(
144                "only {} bytes remaining for stream info, at least {} required {:?}",
145                data.len(),
146                Self::HEADER_SIZE,
147                data
148            );
149            return None;
150        }
151        let result = StreamInfo { data };
152
153        let descriptor_end = Self::HEADER_SIZE + result.es_info_length() as usize;
154        if descriptor_end > data.len() {
155            warn!(
156                "PMT section of size {} is not large enough to contain es_info_length of {}",
157                data.len(),
158                result.es_info_length()
159            );
160            return None;
161        }
162        Some((result, descriptor_end))
163    }
164
165    /// The type of this stream
166    pub fn stream_type(&self) -> StreamType {
167        StreamType(self.data[0])
168    }
169    /// The Pid that will be used for TS packets containing the data of this stream
170    pub fn elementary_pid(&self) -> packet::Pid {
171        packet::Pid::new(u16::from(self.data[1] & 0b0001_1111) << 8 | u16::from(self.data[2]))
172    }
173    fn es_info_length(&self) -> u16 {
174        u16::from(self.data[3] & 0b0000_1111) << 8 | u16::from(self.data[4])
175    }
176
177    /// Returns an iterator over the descriptors attached to this stream
178    pub fn descriptors<Desc: descriptor::Descriptor<'buf> + 'buf>(
179        &self,
180    ) -> impl Iterator<Item = Result<Desc, descriptor::DescriptorError>> + 'buf {
181        let descriptor_end = Self::HEADER_SIZE + self.es_info_length() as usize;
182        descriptor::DescriptorIter::new(&self.data[Self::HEADER_SIZE..descriptor_end])
183    }
184}
185impl<'buf> fmt::Debug for StreamInfo<'buf> {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
187        f.debug_struct("StreamInfo")
188            .field("stream_type", &self.stream_type())
189            .field("elementary_pid", &self.elementary_pid())
190            .field("descriptors", &StreamInfoDescriptorsDebug(self))
191            .finish()
192    }
193}
194struct StreamInfoDescriptorsDebug<'buf>(&'buf StreamInfo<'buf>);
195impl<'buf> fmt::Debug for StreamInfoDescriptorsDebug<'buf> {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
197        f.debug_list()
198            .entries(self.0.descriptors::<descriptor::CoreDescriptors<'buf>>())
199            .finish()
200    }
201}
202
203#[cfg(test)]
204mod test {
205    use crate::demultiplex::test::make_test_data;
206    use crate::demultiplex::DemuxError;
207    use crate::descriptor::CoreDescriptors;
208    use crate::psi::pmt::PmtSection;
209    use assert_matches::assert_matches;
210    use bitstream_io::BitWrite;
211    use hex_literal::hex;
212
213    #[test]
214    fn debug_does_not_panic() {
215        let data = hex!("fd4df0001bfd4df00652010b70010411fd4ef01152010c0a04656e67007c025a007f02068211fd52f01252010d0a04656e67037c035880477f02060606fd51f00d5201055908656e671000010001");
216        let section = PmtSection::from_bytes(&data).unwrap();
217        // don't mind what it looks like, but it's embarrassing if it always panics!
218        assert!(!format!("{:?}", section).is_empty());
219    }
220
221    #[test]
222    fn far_too_small() {
223        let data = hex!("fd4df0");
224        assert_matches!(
225            PmtSection::from_bytes(&data),
226            Err(DemuxError::NotEnoughData {
227                expected: 4,
228                actual: 3,
229                ..
230            })
231        );
232    }
233
234    #[test]
235    fn descriptors_dont_fit() {
236        let data = make_test_data(|w| {
237            w.write(3, 0)?; // reserved
238            w.write(13, 123)?; // PCR_PID
239            w.write(4, 0)?; // reserved
240            w.write(12, 1) // program_info_length
241        });
242        // program_info_length claimed there is 1 byte of descriptor data, but there is no more
243        // data in the buffer
244        assert!(PmtSection::from_bytes(&data).is_err());
245    }
246
247    #[test]
248    fn truncated_descriptor() {
249        let data = make_test_data(|w| {
250            w.write(3, 0)?; // reserved
251            w.write(13, 4)?; // PCR_PID
252            w.write(4, 0)?; // reserved
253            w.write(12, 0x1)?; // program_info_length
254            w.write(8, 0x1) // descriptor_tag
255        });
256        // a descriptor needs to be at least two bytes (descriptor_tag + descriptor_length) but
257        // we only have one (descriptor_length missing)
258        let sect = PmtSection::from_bytes(&data).unwrap();
259        let mut iter = sect.descriptors::<CoreDescriptors<'_>>();
260        assert_matches!(iter.next(), Some(Err(_)));
261        assert_matches!(iter.next(), None);
262    }
263}