Skip to main content

mpeg_ps/
pack_header.rs

1//! Pack Header — ISO/IEC 13818-1 §2.5.3.3, Table 2-39.
2//!
3//! The pack header opens every pack in an MPEG-1/2 Program Stream.
4//! It carries the 42-bit System Clock Reference (SCR), the
5//! `program_mux_rate`, and optional stuffing bytes.
6
7use crate::error::{Error, Result};
8use crate::scr::{self, Scr};
9use broadcast_common::{Parse, Serialize};
10
11/// `pack_start_code` — `0x000001BA`.
12pub const PACK_START_CODE: u32 = 0x0000_01BA;
13
14/// Fixed overhead of the pack header: start_code(4) + SCR field(6) +
15/// mux_rate+reserved+stuffing(4) = 14 bytes.
16const FIXED_LEN: usize = 14;
17
18/// A parsed pack header.
19#[derive(Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21pub struct PackHeader<'a> {
22    /// System Clock Reference (42-bit; 33-bit base + 9-bit extension, 27 MHz).
23    pub scr: Scr,
24    /// Rate at which the P-STD receives the stream during this pack,
25    /// in units of 50 bytes/s. Must be non-zero.
26    pub program_mux_rate: u32,
27    /// The `pack_stuffing_length` field value (≤ 7).
28    pub stuffing_length: u8,
29    /// Stuffing bytes (`0xFF`), zero to seven bytes.
30    #[cfg_attr(feature = "serde", serde(skip))]
31    pub stuffing: &'a [u8],
32    /// Reserved bits (5 bits, preserved for round-trip).
33    pub reserved: u8,
34}
35
36impl PackHeader<'_> {
37    /// Offset to the byte immediately after this pack header (i.e. where PES data starts).
38    #[must_use]
39    pub fn header_len(&self) -> usize {
40        self.serialized_len()
41    }
42}
43
44impl<'a> Parse<'a> for PackHeader<'a> {
45    type Error = Error;
46
47    fn parse(b: &'a [u8]) -> Result<Self> {
48        if b.len() < FIXED_LEN {
49            return Err(Error::BufferTooShort {
50                need: FIXED_LEN,
51                have: b.len(),
52                what: "pack_header",
53            });
54        }
55
56        if u32::from_be_bytes([b[0], b[1], b[2], b[3]]) != PACK_START_CODE {
57            return Err(Error::BadPackStartCode(u32::from_be_bytes([
58                b[0], b[1], b[2], b[3],
59            ])));
60        }
61
62        let scr = scr::read_scr_field(&b[4..10], "SCR")?;
63
64        // Bytes 10..13 (Table 2-39):
65        // byte 10: '01'(2) | mux[21:16](6)
66        // byte 11: mux[15:8](8)
67        // byte 12: mux[7:0](8) — includes the 2 marker bits at bits[1:0]
68        // byte 13: reserved(5, bits[7:3]) | stuffing(3, bits[2:0])
69        //
70        // The 22-bit mux_rate spans the bottom 22 bits of the 3 bytes.
71        // Top 2 bits of byte10 are '01' prefix (like SCR).
72
73        // Validate marker bits at byte12[1:0]
74        if b[12] & 0x03 != 0x03 {
75            return Err(Error::BadMarker("program_mux_rate markers"));
76        }
77
78        let program_mux_rate =
79            ((u32::from(b[10] & 0x3F) << 16) | (u32::from(b[11]) << 8) | u32::from(b[12]))
80                & 0x3F_FFFF;
81
82        if program_mux_rate == 0 {
83            return Err(Error::ZeroMuxRate);
84        }
85
86        let reserved = (b[13] >> 3) & 0x1F;
87        let stuffing_length = b[13] & 0x07;
88
89        let total = FIXED_LEN + stuffing_length as usize;
90        if b.len() < total {
91            return Err(Error::BufferTooShort {
92                need: total,
93                have: b.len(),
94                what: "pack_header stuffing bytes",
95            });
96        }
97
98        Ok(PackHeader {
99            scr,
100            program_mux_rate,
101            stuffing_length,
102            stuffing: &b[FIXED_LEN..total],
103            reserved,
104        })
105    }
106}
107
108impl Serialize for PackHeader<'_> {
109    type Error = Error;
110
111    fn serialized_len(&self) -> usize {
112        FIXED_LEN + self.stuffing_length as usize
113    }
114
115    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
116        let len = self.serialized_len();
117        if buf.len() < len {
118            return Err(Error::BufferTooShort {
119                need: len,
120                have: buf.len(),
121                what: "pack_header serialize output",
122            });
123        }
124
125        // pack_start_code
126        buf[0..4].copy_from_slice(&PACK_START_CODE.to_be_bytes());
127
128        // SCR field (6 bytes)
129        buf[4..10].copy_from_slice(&scr::write_scr_field(self.scr));
130
131        // program_mux_rate: 22 bits across 3 bytes
132        let mux = self.program_mux_rate & 0x3F_FFFF;
133        // byte 10: '01' prefix + mux[21:16]
134        buf[10] = 0x40 | ((mux >> 16) & 0x3F) as u8;
135        // byte 11: mux[15:8]
136        buf[11] = ((mux >> 8) & 0xFF) as u8;
137        // byte 12: mux[7:0] (incl. markers at bits[1:0])
138        buf[12] = (mux & 0xFF) as u8;
139        // byte 13: reserved(5) + stuffing(3)
140        buf[13] = (self.reserved & 0x1F) << 3 | (self.stuffing_length & 0x07);
141
142        // stuffing bytes
143        buf[FIXED_LEN..len].fill(0xFF);
144
145        Ok(len)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use alloc::vec;
153
154    #[test]
155    fn pack_header_round_trip_fixture_pattern() {
156        // Match the fixture pattern: SCR=0, mux_rate=0x03363B, reserved=0x1F, stuffing=0
157        let bytes = vec![
158            0x00, 0x00, 0x01, 0xBA, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, // SCR=0
159            0x43, 0x36, 0x3B, 0xF8, // mux + reserved + stuffing
160        ];
161        let h = PackHeader::parse(&bytes).unwrap();
162        assert_eq!(
163            h.scr,
164            Scr {
165                base: 0,
166                extension: 0,
167            }
168        );
169        assert_eq!(h.program_mux_rate, 0x03363B);
170        assert_eq!(h.stuffing_length, 0);
171        assert_eq!(h.reserved, 0x1F);
172        assert!(h.stuffing.is_empty());
173
174        let mut out = vec![0u8; h.serialized_len()];
175        h.serialize_into(&mut out).unwrap();
176        assert_eq!(&out[..], &bytes[..], "round-trip mismatch");
177
178        // Biting mutation: change mux_rate → output differs
179        let h_mut = PackHeader {
180            program_mux_rate: 0x12345,
181            ..h.clone()
182        };
183        let mut out2 = vec![0u8; h_mut.serialized_len()];
184        h_mut.serialize_into(&mut out2).unwrap();
185        assert_ne!(&out[..], &out2[..]);
186    }
187
188    #[test]
189    fn pack_header_round_trip_with_stuffing() {
190        let bytes = vec![
191            0x00, 0x00, 0x01, 0xBA, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x40, 0x00, 0x43,
192            0x03, // mux=0x43 (LSBs=markers), reserved=0, stuffing=3
193            0xFF, 0xFF, 0xFF,
194        ];
195        let h = PackHeader::parse(&bytes).unwrap();
196        assert_eq!(h.program_mux_rate, 0x43);
197        assert_eq!(h.stuffing_length, 3);
198        assert_eq!(h.stuffing, &[0xFF, 0xFF, 0xFF]);
199        assert_eq!(h.reserved, 0);
200
201        let mut out = vec![0u8; h.serialized_len()];
202        h.serialize_into(&mut out).unwrap();
203        assert_eq!(&out[..], &bytes[..]);
204
205        // Re-parse equals original
206        let h2 = PackHeader::parse(&out).unwrap();
207        assert_eq!(h, h2);
208
209        // Biting mutation: change reserved → output differs
210        let h_mut = PackHeader {
211            reserved: 0x0A,
212            ..h.clone()
213        };
214        let mut out2 = vec![0u8; h_mut.serialized_len()];
215        h_mut.serialize_into(&mut out2).unwrap();
216        assert_ne!(&out[..], &out2[..]);
217    }
218
219    #[test]
220    fn pack_header_nonzero_scr() {
221        let scr = Scr {
222            base: 0x12345678,
223            extension: 0x0AA,
224        };
225        let scr_enc = scr::write_scr_field(scr);
226        let mut b = vec![0u8; 14];
227        b[0..4].copy_from_slice(&PACK_START_CODE.to_be_bytes());
228        b[4..10].copy_from_slice(&scr_enc);
229        b[10..14].copy_from_slice(&[0x40, 0x00, 0x43, 0x00]); // mux=0x43
230
231        let h = PackHeader::parse(&b).unwrap();
232        assert_eq!(h.scr, scr);
233        assert_eq!(h.program_mux_rate, 0x43);
234
235        let mut out = vec![0u8; h.serialized_len()];
236        h.serialize_into(&mut out).unwrap();
237        assert_eq!(&out[..], &b[..]);
238    }
239}