Skip to main content

video_sys/
h264.rs

1use anyhow::{anyhow, bail, Result};
2
3#[derive(Debug, Clone)]
4pub struct H264Config {
5    pub width: u32,
6    pub height: u32,
7
8    /// AVCDecoderConfigurationRecord (aka `avcC` box payload).
9    pub avcc: Vec<u8>,
10
11    /// NAL length field size in bytes (typically 4).
12    pub nal_length_size: usize,
13
14    /// SPS NAL units without start code or length prefix.
15    pub sps: Vec<Vec<u8>>,
16
17    /// PPS NAL units without start code or length prefix.
18    pub pps: Vec<Vec<u8>>,
19}
20
21impl H264Config {
22    pub fn parse_from_avcc(width: u32, height: u32, avcc: &[u8]) -> Result<Self> {
23        if avcc.len() < 7 {
24            bail!("avcC too short");
25        }
26        if avcc[0] != 1 {
27            bail!("unsupported avcC version {}", avcc[0]);
28        }
29
30        // byte 4: 6 bits reserved (111111) + 2 bits lengthSizeMinusOne
31        let nal_length_size = ((avcc[4] & 0b11) as usize) + 1;
32        if nal_length_size < 1 || nal_length_size > 4 {
33            bail!("invalid nal_length_size={}", nal_length_size);
34        }
35
36        let mut off = 5;
37
38        // byte 5: 3 bits reserved (111) + 5 bits numOfSPS
39        if off >= avcc.len() {
40            bail!("avcC truncated");
41        }
42        let num_sps = (avcc[off] & 0b1_1111) as usize;
43        off += 1;
44
45        let mut sps = Vec::with_capacity(num_sps);
46        for _ in 0..num_sps {
47            if off + 2 > avcc.len() {
48                bail!("avcC truncated in SPS length");
49            }
50            let len = u16::from_be_bytes([avcc[off], avcc[off + 1]]) as usize;
51            off += 2;
52            if off + len > avcc.len() {
53                bail!("avcC truncated in SPS data");
54            }
55            sps.push(avcc[off..off + len].to_vec());
56            off += len;
57        }
58
59        if off >= avcc.len() {
60            bail!("avcC truncated before PPS count");
61        }
62        let num_pps = avcc[off] as usize;
63        off += 1;
64
65        let mut pps = Vec::with_capacity(num_pps);
66        for _ in 0..num_pps {
67            if off + 2 > avcc.len() {
68                bail!("avcC truncated in PPS length");
69            }
70            let len = u16::from_be_bytes([avcc[off], avcc[off + 1]]) as usize;
71            off += 2;
72            if off + len > avcc.len() {
73                bail!("avcC truncated in PPS data");
74            }
75            pps.push(avcc[off..off + len].to_vec());
76            off += len;
77        }
78
79        if sps.is_empty() || pps.is_empty() {
80            return Err(anyhow!("avcC must contain at least one SPS and PPS"));
81        }
82
83        Ok(Self {
84            width,
85            height,
86            avcc: avcc.to_vec(),
87            nal_length_size,
88            sps,
89            pps,
90        })
91    }
92
93    /// Microsoft Media Foundation expects MF_MT_MPEG_SEQUENCE_HEADER to contain Annex B SPS/PPS
94    /// concatenated with start codes.
95    pub fn annexb_sequence_header(&self) -> Vec<u8> {
96        let mut out = Vec::new();
97        for ps in self.sps.iter().chain(self.pps.iter()) {
98            out.extend_from_slice(&[0, 0, 0, 1]);
99            out.extend_from_slice(ps);
100        }
101        out
102    }
103
104    /// Convert a MP4/AVCC sample (length-prefixed NAL units) into Annex B (start-code delimited).
105    pub fn avcc_sample_to_annexb(&self, sample: &[u8]) -> Result<Vec<u8>> {
106        let n = self.nal_length_size;
107        if n == 0 || n > 4 {
108            bail!("invalid nal_length_size");
109        }
110
111        let mut off = 0usize;
112        let mut out = Vec::with_capacity(sample.len() + 64);
113
114        while off + n <= sample.len() {
115            let len = match n {
116                1 => sample[off] as usize,
117                2 => u16::from_be_bytes([sample[off], sample[off + 1]]) as usize,
118                3 => ((sample[off] as usize) << 16) | ((sample[off + 1] as usize) << 8) | (sample[off + 2] as usize),
119                4 => u32::from_be_bytes([sample[off], sample[off + 1], sample[off + 2], sample[off + 3]]) as usize,
120                _ => unreachable!(),
121            };
122            off += n;
123
124            if off + len > sample.len() {
125                bail!("avcc sample truncated: off={} len={} size={}", off, len, sample.len());
126            }
127
128            out.extend_from_slice(&[0, 0, 0, 1]);
129            out.extend_from_slice(&sample[off..off + len]);
130            off += len;
131        }
132
133        if off != sample.len() {
134            bail!("avcc sample has trailing bytes: off={} size={}", off, sample.len());
135        }
136
137        Ok(out)
138    }
139}