Skip to main content

mpeg_ps/
program_stream_map.rs

1//! Program Stream Map — ISO/IEC 13818-1 §2.5.4, Table 2-41.
2//!
3//! The PSM provides a mapping from `stream_type`/`elementary_stream_id`
4//! to descriptors, tied to the Program Stream with a CRC-32 trailer.
5//! It uses start-code prefix `0x000001` + `map_stream_id` `0xBC`.
6
7use alloc::vec::Vec;
8
9use crate::error::{Error, Result};
10use broadcast_common::{Parse, Serialize, crc32_mpeg2};
11
12/// `packet_start_code_prefix` — `0x000001`.
13pub const PACKET_START_CODE_PREFIX: u32 = 0x00_0001;
14/// `map_stream_id` — `0xBC`, combined with the prefix forms the PSM start code.
15pub const MAP_STREAM_ID: u8 = 0xBC;
16/// Combined PSM start code: `0x000001BC`.
17#[allow(dead_code)]
18pub const PSM_START_CODE: u32 = (PACKET_START_CODE_PREFIX << 8) | MAP_STREAM_ID as u32;
19
20/// Bytes before the map body: prefix(3) + map_stream_id(1) + psm_length(2) = 6.
21const PREFIX_LEN: usize = 6;
22/// Total fixed header bytes: PREFIX_LEN + flags(1) + reserved(1) + prog_info_len(2) + es_map_len(2) = 13.
23#[allow(dead_code)]
24const HEADER_LEN: usize = 13;
25
26/// An elementary stream descriptor entry with optional stream_id_extension.
27#[derive(Debug, Clone, PartialEq, Eq)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize))]
29pub struct EsMapEntry<'a> {
30    /// `stream_type` per ISO/IEC 13818-1 Table 2-29.
31    pub stream_type: u8,
32    /// `elementary_stream_id`.
33    pub elementary_stream_id: u8,
34    /// If `elementary_stream_id == 0xFD` and `single_extension_stream_flag == 0`,
35    /// this holds the `elementary_stream_id_extension`. Otherwise `None`.
36    pub stream_id_extension: Option<u8>,
37    /// Descriptor bytes for this elementary stream.
38    #[cfg_attr(feature = "serde", serde(skip))]
39    pub descriptors: &'a [u8],
40}
41
42// Owned version for building/serializing
43#[derive(Debug, Clone, PartialEq, Eq)]
44struct OwnedEsMapEntry {
45    pub stream_type: u8,
46    pub elementary_stream_id: u8,
47    pub stream_id_extension: Option<u8>,
48    pub descriptors: Vec<u8>,
49}
50
51/// A parsed Program Stream Map.
52#[derive(Debug, Clone, PartialEq, Eq)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize))]
54pub struct ProgramStreamMap<'a> {
55    /// `current_next_indicator` (1 bit).
56    pub current_next_indicator: bool,
57    /// `single_extension_stream_flag` (1 bit).
58    pub single_extension_stream_flag: bool,
59    /// `program_stream_map_version` (5 bits).
60    pub version: u8,
61    /// Descriptors for the program stream itself.
62    #[cfg_attr(feature = "serde", serde(skip))]
63    pub program_stream_info: &'a [u8],
64    /// Per-elementary-stream entries.
65    pub elementary_stream_map: Vec<EsMapEntry<'a>>,
66    /// The raw CRC-32 value in the trailer (validated on parse).
67    pub crc: u32,
68}
69
70impl<'a> Parse<'a> for ProgramStreamMap<'a> {
71    type Error = Error;
72
73    fn parse(b: &'a [u8]) -> Result<Self> {
74        if b.len() < PREFIX_LEN + 2 + 4 {
75            // prefix + psm_length(2) + CRC(4) = minimum
76            return Err(Error::BufferTooShort {
77                need: PREFIX_LEN + 2 + 4,
78                have: b.len(),
79                what: "program_stream_map",
80            });
81        }
82
83        // packet_start_code_prefix (3 bytes) + map_stream_id (1 byte)
84        let start = u32::from_be_bytes([0, b[0], b[1], b[2]]);
85        if start != PACKET_START_CODE_PREFIX {
86            return Err(Error::BadMapStreamId(b[3]));
87        }
88        if b[3] != MAP_STREAM_ID {
89            return Err(Error::BadMapStreamId(b[3]));
90        }
91
92        let map_length = u16::from_be_bytes([b[4], b[5]]) as usize;
93        let crc_end = PREFIX_LEN + map_length + 4;
94        if b.len() < crc_end {
95            return Err(Error::MapLengthOverflow {
96                map_length,
97                available: b.len().saturating_sub(PREFIX_LEN),
98            });
99        }
100
101        // Validate CRC before trusting the parsed content
102        let crc_data_end = crc_end - 4;
103        let crc_data = &b[0..crc_data_end];
104        let stored_crc = u32::from_be_bytes([
105            b[crc_data_end],
106            b[crc_data_end + 1],
107            b[crc_data_end + 2],
108            b[crc_data_end + 3],
109        ]);
110        let computed_crc = crc32_mpeg2::compute(crc_data);
111        if computed_crc != stored_crc {
112            return Err(Error::BadCrc {
113                computed: computed_crc,
114                stored: stored_crc,
115            });
116        }
117
118        // flags byte: current_next(1) | single_extension(1) | reserved(1) | version(5)
119        let flags = b[6];
120        let current_next_indicator = flags & 0x80 != 0;
121        let single_extension_stream_flag = flags & 0x40 != 0;
122        let version = flags & 0x1F;
123
124        // reserved byte (7 bits reserved + marker_bit)
125        if b[7] & 0x01 == 0 {
126            return Err(Error::BadMarker("program_stream_map marker_bit"));
127        }
128
129        let program_stream_info_length = u16::from_be_bytes([b[8], b[9]]) as usize;
130        let elementary_stream_map_length = u16::from_be_bytes([b[10], b[11]]) as usize;
131
132        let info_start = PREFIX_LEN + 4; // after flags(1)+reserved(1)+prog_info_len(2)
133        let info_end = info_start + program_stream_info_length;
134        // es_map_len field is 2 bytes before es data
135        let es_start = info_end + 2; // after elementary_stream_map_length(2)
136        let es_end = es_start + elementary_stream_map_length;
137
138        if crc_data_end < es_end {
139            return Err(Error::MapLengthOverflow {
140                map_length,
141                available: b.len().saturating_sub(PREFIX_LEN),
142            });
143        }
144
145        let program_stream_info = &b[info_start..info_end];
146
147        // Parse elementary stream loop
148        let es_data = &b[es_start..es_end];
149        let elementary_stream_map = parse_es_loop(es_data, single_extension_stream_flag)?;
150
151        Ok(ProgramStreamMap {
152            current_next_indicator,
153            single_extension_stream_flag,
154            version,
155            program_stream_info,
156            elementary_stream_map,
157            crc: stored_crc,
158        })
159    }
160}
161
162fn parse_es_loop(data: &[u8], single_flag: bool) -> Result<Vec<EsMapEntry<'_>>> {
163    let mut entries = Vec::new();
164    let mut pos = 0;
165    while pos + 4 <= data.len() {
166        let stream_type = data[pos];
167        let elementary_stream_id = data[pos + 1];
168        let es_info_length = u16::from_be_bytes([data[pos + 2], data[pos + 3]]) as usize;
169        let entry_hdr_end = pos + 4;
170        let entry_end = entry_hdr_end + es_info_length;
171        if entry_end > data.len() {
172            return Err(Error::BufferTooShort {
173                need: entry_end,
174                have: data.len(),
175                what: "elementary_stream_map entry descriptors",
176            });
177        }
178
179        let (stream_id_extension, descriptors) = if elementary_stream_id == 0xFD && !single_flag {
180            // Extension form with pseudo descriptor
181            if es_info_length < 3 {
182                return Err(Error::BufferTooShort {
183                    need: 3,
184                    have: es_info_length,
185                    what: "pseudo descriptor for stream_id_extension",
186                });
187            }
188            // pseudo_descriptor_tag + pseudo_descriptor_length + marker+extension
189            if data[entry_hdr_end + 2] & 0x80 == 0 {
190                return Err(Error::BadMarker("elementary_stream_id_extension marker"));
191            }
192            let ext = data[entry_hdr_end + 2] & 0x7F;
193            let desc = &data[entry_hdr_end + 3..entry_end];
194            (Some(ext), desc)
195        } else {
196            let desc = &data[entry_hdr_end..entry_end];
197            (None, desc)
198        };
199
200        entries.push(EsMapEntry {
201            stream_type,
202            elementary_stream_id,
203            stream_id_extension,
204            descriptors,
205        });
206
207        pos = entry_end;
208    }
209    Ok(entries)
210}
211
212fn serialize_es_loop(entries: &[OwnedEsMapEntry]) -> Vec<u8> {
213    let mut buf = Vec::new();
214    for e in entries {
215        buf.push(e.stream_type);
216        buf.push(e.elementary_stream_id);
217
218        let desc_len = if e.stream_id_extension.is_some() {
219            // pseudo descriptor: tag(1) + len(1) + marker+ext(1) + descriptors
220            3 + e.descriptors.len()
221        } else {
222            e.descriptors.len()
223        };
224
225        buf.extend_from_slice(&(desc_len as u16).to_be_bytes());
226
227        if let Some(ext) = e.stream_id_extension {
228            buf.push(0x00); // pseudo_descriptor_tag (any value)
229            buf.push(1 + e.descriptors.len() as u8); // pseudo_descriptor_length
230            buf.push(0x80 | (ext & 0x7F)); // marker + extension
231        }
232
233        buf.extend_from_slice(&e.descriptors);
234    }
235    buf
236}
237
238impl Serialize for ProgramStreamMap<'_> {
239    type Error = Error;
240
241    fn serialized_len(&self) -> usize {
242        let prog_info_len = self.program_stream_info.len();
243        let es_loop_len: usize = self
244            .elementary_stream_map
245            .iter()
246            .map(|e| {
247                4 + if e.stream_id_extension.is_some() {
248                    3 + e.descriptors.len()
249                } else {
250                    e.descriptors.len()
251                }
252            })
253            .sum();
254        // PREFIX_LEN(6) + map_body_len + CRC(4)
255        6 + 6 + prog_info_len + es_loop_len + 4
256    }
257
258    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
259        let total = self.serialized_len();
260        if buf.len() < total {
261            return Err(Error::BufferTooShort {
262                need: total,
263                have: buf.len(),
264                what: "program_stream_map serialize output",
265            });
266        }
267
268        // packet_start_code_prefix (3 bytes)
269        buf[0..3].copy_from_slice(&PACKET_START_CODE_PREFIX.to_be_bytes()[1..]);
270        // map_stream_id
271        buf[3] = MAP_STREAM_ID;
272
273        let prog_info_len = self.program_stream_info.len();
274        let es_loop_data: Vec<u8> = serialize_es_loop(
275            &self
276                .elementary_stream_map
277                .iter()
278                .map(|e| OwnedEsMapEntry {
279                    stream_type: e.stream_type,
280                    elementary_stream_id: e.elementary_stream_id,
281                    stream_id_extension: e.stream_id_extension,
282                    descriptors: e.descriptors.to_vec(),
283                })
284                .collect::<Vec<_>>(),
285        );
286        let es_loop_len = es_loop_data.len();
287
288        // map_length = flags(1) + reserved(1) + prog_info_len(2) + es_loop_len(2) + prog_info + es_loop
289        let map_length = 6 + prog_info_len + es_loop_len;
290        buf[4..6].copy_from_slice(&(map_length as u16).to_be_bytes());
291
292        // flags: current_next(1) + single_extension(1) + reserved(1) + version(5)
293        buf[6] = (u8::from(self.current_next_indicator) << 7)
294            | (u8::from(self.single_extension_stream_flag) << 6)
295            | (self.version & 0x1F);
296
297        // reserved(7) + marker_bit(1)
298        buf[7] = 0x7F | 0x01;
299
300        // program_stream_info_length
301        buf[8..10].copy_from_slice(&(prog_info_len as u16).to_be_bytes());
302        // elementary_stream_map_length
303        buf[10..12].copy_from_slice(&(es_loop_len as u16).to_be_bytes());
304
305        // program_stream_info descriptors
306        buf[12..12 + prog_info_len].copy_from_slice(self.program_stream_info);
307
308        // elementary stream loop
309        let es_start = 12 + prog_info_len;
310        buf[es_start..es_start + es_loop_len].copy_from_slice(&es_loop_data);
311
312        // CRC-32 over everything before it
313        let crc_offset = es_start + es_loop_len;
314        let crc = crc32_mpeg2::compute(&buf[0..crc_offset]);
315        buf[crc_offset..crc_offset + 4].copy_from_slice(&crc.to_be_bytes());
316
317        Ok(total)
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324    use alloc::vec;
325
326    /// Build a valid PSM programmatically and verify round-trip.
327    #[test]
328    fn psm_build_and_round_trip() {
329        let entries = vec![EsMapEntry {
330            stream_type: 0x02, // MPEG-2 video
331            elementary_stream_id: 0xE0,
332            stream_id_extension: None,
333            descriptors: &[0x0A, 0x04, b'H', b'E', b'L', b'L'], // registration descriptor
334        }];
335
336        let psm = ProgramStreamMap {
337            current_next_indicator: true,
338            single_extension_stream_flag: false,
339            version: 3,
340            program_stream_info: &[],
341            elementary_stream_map: entries,
342            crc: 0, // will be overwritten by serialize
343        };
344
345        let mut buf = vec![0u8; psm.serialized_len()];
346        psm.serialize_into(&mut buf).unwrap();
347
348        // Parse back
349        let parsed = ProgramStreamMap::parse(&buf).unwrap();
350        assert!(parsed.current_next_indicator);
351        assert!(!parsed.single_extension_stream_flag);
352        assert_eq!(parsed.version, 3);
353        assert!(parsed.program_stream_info.is_empty());
354        assert_eq!(parsed.elementary_stream_map.len(), 1);
355        assert_eq!(parsed.elementary_stream_map[0].stream_type, 0x02);
356        assert_eq!(parsed.elementary_stream_map[0].elementary_stream_id, 0xE0);
357        assert!(
358            parsed.elementary_stream_map[0]
359                .stream_id_extension
360                .is_none()
361        );
362        assert_eq!(
363            parsed.elementary_stream_map[0].descriptors,
364            &[0x0A, 0x04, b'H', b'E', b'L', b'L']
365        );
366
367        // Byte-exact round-trip
368        let mut out2 = vec![0u8; parsed.serialized_len()];
369        parsed.serialize_into(&mut out2).unwrap();
370        assert_eq!(&out2[..], &buf[..], "byte-exact round-trip mismatch");
371
372        // Mutation test: change version, output must differ
373        let psm_mut = ProgramStreamMap {
374            current_next_indicator: true,
375            single_extension_stream_flag: false,
376            version: 7,
377            program_stream_info: &[],
378            elementary_stream_map: vec![EsMapEntry {
379                stream_type: 0x02,
380                elementary_stream_id: 0xE0,
381                stream_id_extension: None,
382                descriptors: &[0x0A, 0x04, b'H', b'E', b'L', b'L'],
383            }],
384            crc: 0,
385        };
386        let mut out3 = vec![0u8; psm_mut.serialized_len()];
387        psm_mut.serialize_into(&mut out3).unwrap();
388        assert_ne!(&buf[..], &out3[..]);
389    }
390
391    #[test]
392    fn psm_with_stream_id_extension() {
393        let entries = vec![EsMapEntry {
394            stream_type: 0x06, // subtitles
395            elementary_stream_id: 0xFD,
396            stream_id_extension: Some(0x0F),
397            descriptors: &[0x59, 0x02, 0x01, 0x02], // subtitling descriptor
398        }];
399
400        let psm = ProgramStreamMap {
401            current_next_indicator: true,
402            single_extension_stream_flag: false,
403            version: 1,
404            program_stream_info: &[],
405            elementary_stream_map: entries,
406            crc: 0,
407        };
408
409        let mut buf = vec![0u8; psm.serialized_len()];
410        psm.serialize_into(&mut buf).unwrap();
411
412        let parsed = ProgramStreamMap::parse(&buf).unwrap();
413        assert_eq!(parsed.elementary_stream_map.len(), 1);
414        assert_eq!(
415            parsed.elementary_stream_map[0].stream_id_extension,
416            Some(0x0F)
417        );
418        assert_eq!(
419            parsed.elementary_stream_map[0].descriptors,
420            &[0x59, 0x02, 0x01, 0x02]
421        );
422
423        // Byte-exact round-trip
424        let mut out2 = vec![0u8; parsed.serialized_len()];
425        parsed.serialize_into(&mut out2).unwrap();
426        assert_eq!(&out2[..], &buf[..]);
427    }
428
429    #[test]
430    fn psm_bad_crc_rejected() {
431        let psm = ProgramStreamMap {
432            current_next_indicator: true,
433            single_extension_stream_flag: false,
434            version: 0,
435            program_stream_info: &[],
436            elementary_stream_map: vec![],
437            crc: 0,
438        };
439        let mut buf = vec![0u8; psm.serialized_len()];
440        psm.serialize_into(&mut buf).unwrap();
441        // Corrupt CRC (last 4 bytes)
442        let crc_off = buf.len() - 4;
443        buf[crc_off] ^= 0xFF;
444        assert!(matches!(
445            ProgramStreamMap::parse(&buf),
446            Err(Error::BadCrc { .. })
447        ));
448    }
449}