Skip to main content

codec/pixel_format/
mpeg2.rs

1//! MPEG-2 sequence header parser — ISO/IEC 13818-2 §6.2.2.1 + §6.2.2.3.
2
3use super::bitreader::BitReader;
4
5/// Parsed MPEG-2 sequence header + (optional) sequence extension.
6///
7/// MPEG-2 video §6.2.2.1/§6.2.2.3 (ISO/IEC 13818-2): the 12-bit
8/// `horizontal_size_value` / `vertical_size_value` from the sequence
9/// header, optionally extended to 14 bits by the 2-bit
10/// `horizontal_size_extension` / `vertical_size_extension` fields in a
11/// `sequence_extension()` start-code-prefixed NAL. Pure MPEG-1
12/// (start code 0xB3 but no 0xB5 extension) stays 12-bit — produces
13/// the same 12-bit result via the extension-less path.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct Mpeg2SeqInfo {
16    pub width: u32,
17    pub height: u32,
18}
19
20/// MPEG-2 sequence header scan — ISO/IEC 13818-2 §6.2.2.1 (sequence
21/// header, start code `00 00 01 B3`) + §6.2.2.3 (sequence extension,
22/// start code `00 00 01 B5` with `extension_start_code_identifier==1`).
23///
24/// The sequence header carries 12-bit `horizontal_size_value` and
25/// `vertical_size_value`, tight for sizes ≤ 4095. The optional sequence
26/// extension prepends 2-bit `_extension` fields that, when combined,
27/// bring the total to 14 bits (sizes ≤ 16383). Pure MPEG-1 (start code
28/// 0xB3 only, no 0xB5) never has the extension and stays 12-bit.
29pub fn parse_mpeg2_sequence_header(sample: &[u8]) -> Option<Mpeg2SeqInfo> {
30    // Walk bytes looking for 00 00 01 B3 (sequence_header_code). The
31    // following 3 bytes carry horizontal(12) + vertical(12).
32    let seq_hdr_start = find_mpeg2_start_code(sample, 0xB3)?;
33    let hdr_body_off = seq_hdr_start + 4;
34    if hdr_body_off + 3 > sample.len() {
35        return None;
36    }
37    let b = &sample[hdr_body_off..hdr_body_off + 3];
38    let mut width = (((b[0] as u32) << 4) | ((b[1] as u32) >> 4)) & 0x0FFF;
39    let mut height = (((b[1] as u32 & 0x0F) << 8) | (b[2] as u32)) & 0x0FFF;
40
41    // Look for a subsequent sequence_extension that upgrades the 12-bit
42    // values to 14-bit. Only scan forward from seq_hdr_start; a
43    // sequence_extension before the first sequence_header is
44    // nonsensical and we shouldn't confuse the parse.
45    let search_from = hdr_body_off + 3;
46    if search_from < sample.len()
47        && let Some(ext_start) = find_mpeg2_start_code(&sample[search_from..], 0xB5)
48    {
49        let ext_body_off = search_from + ext_start + 4;
50        if ext_body_off + 3 <= sample.len() {
51            let mut br = BitReader::new(&sample[ext_body_off..]);
52            if let Some(id) = br.read_bits(4)
53                && id == 1
54            {
55                // sequence_extension §6.2.2.3:
56                //   extension_start_code_identifier  u(4) = 0001   (already read)
57                //   profile_and_level_indication     u(8)
58                //   progressive_sequence             u(1)
59                //   chroma_format                    u(2)
60                //   horizontal_size_extension        u(2)
61                //   vertical_size_extension          u(2)
62                let _profile_level = br.read_bits(8)?;
63                let _progressive = br.read_bits(1)?;
64                let _chroma = br.read_bits(2)?;
65                let h_ext = br.read_bits(2)?;
66                let v_ext = br.read_bits(2)?;
67                width |= h_ext << 12;
68                height |= v_ext << 12;
69            }
70        }
71    }
72
73    if width == 0 || height == 0 {
74        return None;
75    }
76    Some(Mpeg2SeqInfo { width, height })
77}
78
79/// Scan for an MPEG-2 start code (0x00 0x00 0x01 <target>) byte-aligned.
80/// Returns the file offset of the leading 0x00 on success.
81fn find_mpeg2_start_code(data: &[u8], target: u8) -> Option<usize> {
82    let mut i = 0;
83    while i + 4 <= data.len() {
84        if data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 && data[i + 3] == target {
85            return Some(i);
86        }
87        i += 1;
88    }
89    None
90}