Skip to main content

mpeg_ps/
system_header.rs

1//! System Header — ISO/IEC 13818-1 §2.5.3.5, Table 2-40.
2//!
3//! The (optional) system header follows immediately after the pack header
4//! stuffing bytes, in the first pack of the stream. It constrains the P-STD
5//! model: `rate_bound`, `audio_bound`/`video_bound`, and per-stream P-STD
6//! buffer-size bounds.
7
8use alloc::vec::Vec;
9
10use crate::error::{Error, Result};
11use dvb_common::{Parse, Serialize};
12
13/// `system_header_start_code` — `0x000001BB`.
14pub const SYSTEM_HEADER_START_CODE: u32 = 0x0000_01BB;
15
16/// `stream_id` value that triggers the extension (extended_stream_id) form.
17const EXT_STREAM_ID: u8 = 0xB7;
18
19/// Fixed bytes before the stream loop: start_code(4) + header_length(2).
20const PREFIX_LEN: usize = 6;
21
22/// A per-stream P-STD buffer bound entry.
23#[derive(Debug, Clone, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize))]
25pub struct StdBufferBound {
26    /// The stream's `stream_id` (ISO/IEC 13818-1 Table 2-22).
27    pub stream_id: u8,
28    /// If `stream_id == 0xB7`, this is the extended `stream_id_extension` field.
29    /// Otherwise `None`.
30    pub stream_id_extension: Option<u8>,
31    /// `P-STD_buffer_bound_scale`: `false` = 128 bytes, `true` = 1024 bytes.
32    pub buffer_bound_scale: bool,
33    /// `P-STD_buffer_size_bound` in units of `buffer_bound_scale`.
34    pub buffer_size_bound: u16,
35}
36
37/// A parsed system header.
38#[derive(Debug, Clone, PartialEq, Eq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize))]
40pub struct SystemHeader {
41    /// Upper bound on `program_mux_rate` across all packs (22-bit, 50 B/s units).
42    pub rate_bound: u32,
43    /// Upper bound on simultaneously-active audio streams (6-bit).
44    pub audio_bound: u8,
45    /// Fixed / variable bitrate indicator.
46    pub fixed_flag: bool,
47    /// Constrained system parameters flag.
48    pub csps_flag: bool,
49    /// System audio lock flag.
50    pub system_audio_lock_flag: bool,
51    /// System video lock flag.
52    pub system_video_lock_flag: bool,
53    /// Upper bound on simultaneously-active video streams (5-bit).
54    pub video_bound: u8,
55    /// Packet rate restriction flag.
56    pub packet_rate_restriction_flag: bool,
57    /// Per-stream P-STD buffer bounds (the `while (nextbits() == '1')` loop).
58    pub std_buffer_bounds: Vec<StdBufferBound>,
59}
60
61fn stream_loop_len(system_header: &SystemHeader) -> u16 {
62    let mut len: u16 = 0;
63    for b in &system_header.std_buffer_bounds {
64        if b.stream_id_extension.is_some() {
65            len += 6;
66        } else {
67            len += 3;
68        }
69    }
70    len
71}
72
73impl<'a> Parse<'a> for SystemHeader {
74    type Error = Error;
75
76    fn parse(b: &'a [u8]) -> Result<Self> {
77        if b.len() < PREFIX_LEN {
78            return Err(Error::BufferTooShort {
79                need: PREFIX_LEN,
80                have: b.len(),
81                what: "system_header prefix",
82            });
83        }
84
85        if u32::from_be_bytes([b[0], b[1], b[2], b[3]]) != SYSTEM_HEADER_START_CODE {
86            return Err(Error::BadSystemHeaderStartCode(u32::from_be_bytes([
87                b[0], b[1], b[2], b[3],
88            ])));
89        }
90
91        let header_length = u16::from_be_bytes([b[4], b[5]]) as usize;
92        let body_end = PREFIX_LEN + header_length;
93        if b.len() < body_end {
94            return Err(Error::HeaderLengthOverflow {
95                header_length,
96                available: b.len().saturating_sub(PREFIX_LEN),
97            });
98        }
99        let body = &b[PREFIX_LEN..body_end];
100
101        // Wire layout (Table 2-40, after header_length):
102        // byte 0: marker(1)  | rate_bound[21:15](7)
103        // byte 1: rate_bound[14:7](8)
104        // byte 2: rate_bound[6:0](7) | marker(1)
105        // byte 3: audio_bound[5:0](6) | fixed_flag(1) | CSPS_flag(1)
106        // byte 4: system_audio_lock_flag(1) | system_video_lock_flag(1) | marker(1) | video_bound[4:0](5)
107        // byte 5: packet_rate_restriction_flag(1) | reserved(7)
108
109        if body.len() < 6 {
110            return Err(Error::BufferTooShort {
111                need: 6 + PREFIX_LEN,
112                have: b.len(),
113                what: "system_header fixed body",
114            });
115        }
116
117        // Marker bit checks
118        if body[0] & 0x80 == 0 {
119            return Err(Error::BadMarker("system_header rate_bound marker 1"));
120        }
121        if body[2] & 0x01 == 0 {
122            return Err(Error::BadMarker("system_header rate_bound marker 2"));
123        }
124        if body[4] & 0x20 == 0 {
125            return Err(Error::BadMarker("system_header video_bound marker"));
126        }
127
128        // rate_bound: 22 bits
129        let rate_bound = ((u32::from(body[0] & 0x7F) << 15)
130            | (u32::from(body[1]) << 7)
131            | u32::from(body[2] >> 1))
132            & 0x3F_FFFF;
133
134        // byte 3: audio_bound(6) | fixed_flag(1) | CSPS_flag(1)
135        let audio_bound = (body[3] >> 2) & 0x3F;
136        let fixed_flag = body[3] & 0x02 != 0;
137        let csps_flag = body[3] & 0x01 != 0;
138
139        // byte 4: system_audio_lock_flag(1) | system_video_lock_flag(1) | marker(1) | video_bound[4:0](5)
140        let system_audio_lock_flag = body[4] & 0x80 != 0;
141        let system_video_lock_flag = body[4] & 0x40 != 0;
142        let video_bound = body[4] & 0x0F;
143
144        // byte 5: packet_rate_restriction_flag(1) | reserved(7)
145        let packet_rate_restriction_flag = body[5] & 0x80 != 0;
146
147        // Stream loop — each entry starts with MSB=1 (nextbits()=='1')
148        let mut pos = 6;
149        let mut std_buffer_bounds = Vec::new();
150        while pos < body.len() && body[pos] & 0x80 != 0 {
151            let stream_id = body[pos];
152            if stream_id == EXT_STREAM_ID {
153                // Extension form (6 bytes)
154                if pos + 6 > body.len() {
155                    return Err(Error::BufferTooShort {
156                        need: pos + 6,
157                        have: body.len(),
158                        what: "system_header extended stream entry",
159                    });
160                }
161                // byte pos+1: '11' + '000 0000'(5 bits)
162                if body[pos + 1] & 0xC0 != 0xC0 {
163                    return Err(Error::BadStreamIdExtensionPrefix(body[pos + 1]));
164                }
165                // byte pos+2: '000 0000'(1) + stream_id_extension(7)
166                let stream_id_extension = body[pos + 2] & 0x7F;
167                // byte pos+3: '1011 0110'
168                if body[pos + 3] != 0xB6 {
169                    return Err(Error::BadStreamIdExtensionPrefix(body[pos + 3]));
170                }
171                // byte pos+4: '11' + scale(1) + size[12:7](5)
172                if body[pos + 4] & 0xC0 != 0xC0 {
173                    return Err(Error::BadMarker("P-STD_buffer_bound_scale prefix (ext)"));
174                }
175                let buffer_bound_scale = body[pos + 4] & 0x20 != 0;
176                // byte pos+5: size[6:0](7) + marker(1)? No — the 13-bit size fits in the remaining bits
177                // byte pos+4 has 5 bits of size; byte pos+5 has 7 bits + marker at bit0?
178                // Actually: P-STD_buffer_bound_scale(1) + P-STD_buffer_size_bound(13) = 14 bits
179                // byte pos+4: '11'(2) | scale(1) | size[12:7](5) = 8 bits
180                // byte pos+5: size[6:0](7) | marker?
181                // From Table 2-40: after the scale+size, the stream loop tests nextbits()=='1' so
182                // the next byte's MSB must be set. But the size is 13 bits — only 12 fit in bytes 4-5.
183                // Wait: scale(1) + size(13) = 14 bits. byte4 has 5 bits of size after 3 used bits.
184                // byte5 has all 8 bits = 5+8=13 bits of size. No marker.
185                let buffer_size_bound =
186                    (u16::from(body[pos + 4] & 0x1F) << 8) | u16::from(body[pos + 5]);
187                std_buffer_bounds.push(StdBufferBound {
188                    stream_id,
189                    stream_id_extension: Some(stream_id_extension),
190                    buffer_bound_scale,
191                    buffer_size_bound,
192                });
193                pos += 6;
194            } else {
195                // Normal form (3 bytes)
196                if pos + 3 > body.len() {
197                    return Err(Error::BufferTooShort {
198                        need: pos + 3,
199                        have: body.len(),
200                        what: "system_header stream entry",
201                    });
202                }
203                // byte pos+1: '11' + scale(1) + size[12:7](5)
204                if body[pos + 1] & 0xC0 != 0xC0 {
205                    return Err(Error::BadMarker("P-STD_buffer_bound_scale prefix"));
206                }
207                let buffer_bound_scale = body[pos + 1] & 0x20 != 0;
208                // byte pos+2: size[6:0](7) — no marker in normal form either
209                // Actually: for the non-ext form too, the 13-bit size spans 5 bits in byte1 + 8 in byte2
210                let buffer_size_bound =
211                    (u16::from(body[pos + 1] & 0x1F) << 8) | u16::from(body[pos + 2]);
212                std_buffer_bounds.push(StdBufferBound {
213                    stream_id,
214                    stream_id_extension: None,
215                    buffer_bound_scale,
216                    buffer_size_bound,
217                });
218                pos += 3;
219            }
220        }
221
222        Ok(SystemHeader {
223            rate_bound,
224            audio_bound,
225            fixed_flag,
226            csps_flag,
227            system_audio_lock_flag,
228            system_video_lock_flag,
229            video_bound,
230            packet_rate_restriction_flag,
231            std_buffer_bounds,
232        })
233    }
234}
235
236impl Serialize for SystemHeader {
237    type Error = Error;
238
239    fn serialized_len(&self) -> usize {
240        PREFIX_LEN + 6 + stream_loop_len(self) as usize
241    }
242
243    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
244        let total = self.serialized_len();
245        if buf.len() < total {
246            return Err(Error::BufferTooShort {
247                need: total,
248                have: buf.len(),
249                what: "system_header serialize output",
250            });
251        }
252
253        // system_header_start_code
254        buf[0..4].copy_from_slice(&SYSTEM_HEADER_START_CODE.to_be_bytes());
255
256        // header_length (bytes after this field)
257        let header_length = 6 + stream_loop_len(self);
258        buf[4..6].copy_from_slice(&header_length.to_be_bytes());
259
260        let rate_bound = self.rate_bound & 0x3F_FFFF;
261        // byte 6: marker(1) | rate_bound[21:15](7)
262        buf[6] = 0x80 | ((rate_bound >> 15) & 0x7F) as u8;
263        // byte 7: rate_bound[14:7]
264        buf[7] = ((rate_bound >> 7) & 0xFF) as u8;
265        // byte 8: rate_bound[6:0](7) | marker(1)
266        buf[8] = (((rate_bound & 0x7F) as u8) << 1) | 0x01;
267        // byte 9: audio_bound[5:0](6) | fixed_flag(1) | CSPS_flag(1)
268        buf[9] = (self.audio_bound & 0x3F) << 2
269            | (u8::from(self.fixed_flag) << 1)
270            | u8::from(self.csps_flag);
271        // byte 10: system_audio_lock_flag(1) | system_video_lock_flag(1) | marker(1) | video_bound[4:0](5)
272        buf[10] = (u8::from(self.system_audio_lock_flag) << 7)
273            | (u8::from(self.system_video_lock_flag) << 6)
274            | 0x20 // marker_bit (bit5 of byte 10)
275            | (self.video_bound & 0x1F);
276        // byte 11: packet_rate_restriction_flag(1) | reserved(7)
277        buf[11] = (u8::from(self.packet_rate_restriction_flag) << 7) | 0x7F;
278
279        // Stream loop
280        let mut pos = 12;
281        for bound in &self.std_buffer_bounds {
282            if let Some(ext) = bound.stream_id_extension {
283                buf[pos] = bound.stream_id;
284                buf[pos + 1] = 0xC0;
285                buf[pos + 2] = ext & 0x7F;
286                buf[pos + 3] = 0xB6;
287                buf[pos + 4] = 0xC0
288                    | (u8::from(bound.buffer_bound_scale) << 5)
289                    | ((bound.buffer_size_bound >> 8) & 0x1F) as u8;
290                buf[pos + 5] = (bound.buffer_size_bound & 0xFF) as u8;
291                pos += 6;
292            } else {
293                buf[pos] = bound.stream_id;
294                buf[pos + 1] = 0xC0
295                    | (u8::from(bound.buffer_bound_scale) << 5)
296                    | ((bound.buffer_size_bound >> 8) & 0x1F) as u8;
297                buf[pos + 2] = (bound.buffer_size_bound & 0xFF) as u8;
298                pos += 3;
299            }
300        }
301
302        Ok(total)
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309    use alloc::vec;
310
311    #[test]
312    fn system_header_round_trip_no_streams() {
313        let bytes = vec![
314            0x00, 0x00, 0x01, 0xBB, // start_code
315            0x00, 0x06, // header_length = 6 (just the fixed part)
316            0x80, 0x00, 0x01, // rate_bound=0, markers
317            0x04, // audio_bound=1, fixed=0, CSPS=0
318            0x20, // audio_lock=0, video_lock=0, marker=1, video_bound=0
319            0xFF, // packet_rate=1, reserved=0x7F
320        ];
321        let h = SystemHeader::parse(&bytes).unwrap();
322        assert_eq!(h.rate_bound, 0);
323        assert_eq!(h.audio_bound, 1);
324        assert!(!h.fixed_flag);
325        assert!(!h.csps_flag);
326        assert!(!h.system_audio_lock_flag);
327        assert!(!h.system_video_lock_flag);
328        assert_eq!(h.video_bound, 0);
329        assert!(h.packet_rate_restriction_flag);
330        assert!(h.std_buffer_bounds.is_empty());
331
332        let mut out = vec![0u8; h.serialized_len()];
333        h.serialize_into(&mut out).unwrap();
334        assert_eq!(&out[..], &bytes[..]);
335
336        let h2 = SystemHeader::parse(&out).unwrap();
337        assert_eq!(h, h2);
338    }
339
340    #[test]
341    fn system_header_round_trip_with_streams() {
342        let bytes = vec![
343            0x00, 0x00, 0x01, 0xBB, 0x00,
344            0x0C, // header_length = 12 (6 fixed + 6 for 2 streams)
345            0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
346            // stream 1: stream_id=0xE0, scale=1, size=0x1FFF
347            0xE0, 0xFF, 0xFF, // stream 2: stream_id=0xC0, scale=0, size=0x0100
348            0xC0, 0xC1, 0x00,
349        ];
350        let h = SystemHeader::parse(&bytes).unwrap();
351        assert_eq!(h.std_buffer_bounds.len(), 2);
352        assert_eq!(h.std_buffer_bounds[0].stream_id, 0xE0);
353        assert!(h.std_buffer_bounds[0].stream_id_extension.is_none());
354        assert!(h.std_buffer_bounds[0].buffer_bound_scale);
355        assert_eq!(h.std_buffer_bounds[0].buffer_size_bound, 0x1FFF);
356
357        assert_eq!(h.std_buffer_bounds[1].stream_id, 0xC0);
358        assert!(h.std_buffer_bounds[1].stream_id_extension.is_none());
359        assert!(!h.std_buffer_bounds[1].buffer_bound_scale);
360        assert_eq!(h.std_buffer_bounds[1].buffer_size_bound, 0x0100);
361
362        let mut out = vec![0u8; h.serialized_len()];
363        h.serialize_into(&mut out).unwrap();
364        assert_eq!(&out[..], &bytes[..]);
365
366        let h2 = SystemHeader::parse(&out).unwrap();
367        assert_eq!(h, h2);
368
369        // Mutation test
370        let h_mut = SystemHeader {
371            rate_bound: 12345,
372            ..h.clone()
373        };
374        let mut out2 = vec![0u8; h_mut.serialized_len()];
375        h_mut.serialize_into(&mut out2).unwrap();
376        assert_ne!(&out[..], &out2[..]);
377    }
378
379    #[test]
380    fn system_header_round_trip_extended_stream_id() {
381        let bytes = vec![
382            0x00, 0x00, 0x01, 0xBB, 0x00, 0x0C, // header_length = 12
383            0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
384            // extended stream: stream_id=0xB7, ext=0x05, scale=1, size=0x0100
385            0xB7, 0xC0, 0x05, 0xB6, 0xE1, 0x00,
386        ];
387        let h = SystemHeader::parse(&bytes).unwrap();
388        assert_eq!(h.std_buffer_bounds.len(), 1);
389        assert_eq!(h.std_buffer_bounds[0].stream_id, 0xB7);
390        assert_eq!(h.std_buffer_bounds[0].stream_id_extension, Some(0x05));
391        assert!(h.std_buffer_bounds[0].buffer_bound_scale);
392        assert_eq!(h.std_buffer_bounds[0].buffer_size_bound, 0x100);
393
394        let mut out = vec![0u8; h.serialized_len()];
395        h.serialize_into(&mut out).unwrap();
396        assert_eq!(&out[..], &bytes[..]);
397    }
398}