1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Exp-Golomb bit reader over an RBSP byte stream.
//!
//! RBSP (Raw Byte Sequence Payload) requires **emulation prevention byte removal**
//! before parsing: every `00 00 03` triplet in the NAL unit byte stream is replaced
//! by `00 00` (the `03` is discarded). This is done once when constructing the
//! reader via [`BitReader::with_unescape`].
//!
//! Supports `ue(v)` (unsigned) and `se(v)` (signed) Exp-Golomb coding per
//! ITU-T H.264 §9.1 and H.265 §9.2.2.
use crate::error::{Error, Result};
use alloc::vec::Vec;
/// Unescape a NAL unit byte stream into an RBSP: remove every `00 00 03` → `00 00`.
fn unescape(nal: &[u8]) -> Vec<u8> {
let n = nal.len();
let mut out = Vec::with_capacity(n);
let mut i = 0;
while i < n {
if i + 2 < n && nal[i] == 0 && nal[i + 1] == 0 && nal[i + 2] == 3 {
out.push(0);
out.push(0);
i += 3;
} else {
out.push(nal[i]);
i += 1;
}
}
out
}
/// Bit-level reader over an RBSP buffer (after emulation-prevention byte removal).
///
/// Reads bits from left to right within each byte (MSB-first, big-endian
/// bit numbering — ITU-T H.264 §7.2).
///
/// The struct owns the RBSP `Vec<u8>` and tracks the current bit position.
pub struct BitReader {
data: Vec<u8>,
bit_pos: usize,
}
impl BitReader {
/// Create a new reader from already-unescaped RBSP bytes (no lifetime
/// entanglement — the data is copied into the reader).
pub fn from_rbsp(data: &[u8], what: &'static str) -> Result<Self> {
if data.is_empty() {
return Err(Error::BufferTooShort {
need: 1,
have: 0,
what,
});
}
Ok(Self {
data: data.to_vec(),
bit_pos: 0,
})
}
/// Create a new reader from a NAL unit body (after the NAL header),
/// with emulation-prevention byte removal.
pub fn with_unescape(nal_body: &[u8], what: &'static str) -> Result<Self> {
let rbsp = unescape(nal_body);
if rbsp.is_empty() {
return Err(Error::BufferTooShort {
need: 1,
have: 0,
what,
});
}
Ok(Self {
data: rbsp,
bit_pos: 0,
})
}
fn has_bits(&self, n: usize) -> bool {
self.bit_pos + n <= self.data.len() * 8
}
/// Read `n` bits as an unsigned integer (`u(n)` / `f(n)`).
///
/// Bounds are checked here exactly as before; the extraction itself
/// delegates to `broadcast_common::bits::BitReader` (shared with
/// `dvb-t2mi`/`rdd29`/`st291`) so a bit-order/overrun fix there reaches
/// this reader too. This type stays its own owning wrapper around that
/// shared cursor (rather than holding one directly) because it owns the
/// unescaped RBSP `Vec<u8>` the shared, borrowing `BitReader<'a>` cannot
/// — an owned buffer and a reader borrowing from it can't live in the
/// same struct without self-referential lifetimes.
pub fn read_bits(&mut self, n: usize, what: &'static str) -> Result<u64> {
if n > 64 || !self.has_bits(n) {
return Err(Error::BufferTooShort {
need: self.bit_pos + n,
have: self.data.len() * 8,
what,
});
}
if n == 0 {
return Ok(0);
}
let mut br = broadcast_common::bits::BitReader::new(&self.data);
br.skip_bits(self.bit_pos)
.expect("bounds already validated above");
let val = br
.read_bits(n as u32)
.expect("bounds already validated above");
self.bit_pos += n;
Ok(val)
}
/// Read one bit as a `bool`.
pub fn read_flag(&mut self, what: &'static str) -> Result<bool> {
Ok(self.read_bits(1, what)? != 0)
}
/// Consume padding bits up to the next byte boundary (e.g.
/// `gci_alignment_zero_bit` / `ptl_reserved_zero_bit`, H.266 §7.3.3).
pub fn align_to_byte(&mut self, what: &'static str) -> Result<()> {
while !self.bit_pos.is_multiple_of(8) {
let _ = self.read_bits(1, what)?;
}
Ok(())
}
/// Parse `ue(v)` — unsigned integer Exp-Golomb-coded syntax element.
///
/// H.264 §9.1: leadingZeroBits (count of zero bits before the first 1-bit),
/// then read that many bits as the unsigned value `codeNum`.
pub fn read_ue(&mut self, what: &'static str) -> Result<u64> {
let mut leading_zero_bits: u32 = 0;
while self.has_bits(1) && self.read_bits(1, what)? == 0 {
leading_zero_bits += 1;
}
if leading_zero_bits > 0 && !self.has_bits(leading_zero_bits as usize) {
return Err(Error::BufferTooShort {
need: self.bit_pos + leading_zero_bits as usize,
have: self.data.len() * 8,
what,
});
}
if leading_zero_bits == 0 {
return Ok(0);
}
let info = self.read_bits(leading_zero_bits as usize, what)?;
Ok((1u64 << leading_zero_bits) - 1 + info)
}
/// Parse `se(v)` — signed integer Exp-Golomb-coded syntax element.
///
/// H.264 §9.1.1: mapping from `codeNum` to signed value.
pub fn read_se(&mut self, what: &'static str) -> Result<i64> {
let code_num = self.read_ue(what)?;
if code_num & 1 == 0 {
Ok(-((code_num >> 1) as i64))
} else {
Ok(((code_num + 1) >> 1) as i64)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unescape_removes_emulation_prevention_bytes() {
let nal = [0x00, 0x00, 0x03, 0x04, 0x00, 0x00];
let rbsp = unescape(&nal);
assert_eq!(rbsp, &[0x00, 0x00, 0x04, 0x00, 0x00]);
}
#[test]
fn unescape_no_epb_passthrough() {
let nal = [0x01, 0x02, 0x03, 0x04];
let rbsp = unescape(&nal);
assert_eq!(rbsp, nal);
}
#[test]
fn ue_simple() {
let mut r = BitReader::from_rbsp(&[0x80], "test").unwrap();
assert_eq!(r.read_ue("test").unwrap(), 0);
}
#[test]
fn ue_value_3() {
let mut r = BitReader::from_rbsp(&[0x20], "test").unwrap();
assert_eq!(r.read_ue("test").unwrap(), 3);
}
#[test]
fn se_signed() {
let mut r = BitReader::from_rbsp(&[0x80], "test").unwrap();
assert_eq!(r.read_se("test").unwrap(), 0);
let mut r = BitReader::from_rbsp(&[0x40], "test").unwrap();
assert_eq!(r.read_se("test").unwrap(), 1);
let mut r = BitReader::from_rbsp(&[0x60], "test").unwrap();
assert_eq!(r.read_se("test").unwrap(), -1);
}
}