oximedia_codec/av1/
obu.rs1use crate::error::{CodecError, CodecResult};
7use oximedia_io::BitReader;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub enum ObuType {
12 Reserved0,
14 SequenceHeader,
16 TemporalDelimiter,
18 FrameHeader,
20 TileGroup,
22 Metadata,
24 Frame,
26 RedundantFrameHeader,
28 TileList,
30 Reserved(u8),
32 Padding,
34}
35
36impl From<u8> for ObuType {
37 fn from(value: u8) -> Self {
38 match value {
39 0 => Self::Reserved0,
40 1 => Self::SequenceHeader,
41 2 => Self::TemporalDelimiter,
42 3 => Self::FrameHeader,
43 4 => Self::TileGroup,
44 5 => Self::Metadata,
45 6 => Self::Frame,
46 7 => Self::RedundantFrameHeader,
47 8 => Self::TileList,
48 15 => Self::Padding,
49 other => Self::Reserved(other),
50 }
51 }
52}
53
54impl From<ObuType> for u8 {
55 fn from(obu_type: ObuType) -> Self {
56 match obu_type {
57 ObuType::Reserved0 => 0,
58 ObuType::SequenceHeader => 1,
59 ObuType::TemporalDelimiter => 2,
60 ObuType::FrameHeader => 3,
61 ObuType::TileGroup => 4,
62 ObuType::Metadata => 5,
63 ObuType::Frame => 6,
64 ObuType::RedundantFrameHeader => 7,
65 ObuType::TileList => 8,
66 ObuType::Padding => 15,
67 ObuType::Reserved(v) => v,
68 }
69 }
70}
71
72#[derive(Clone, Debug)]
74pub struct ObuHeader {
75 pub obu_type: ObuType,
77 pub has_extension: bool,
79 pub has_size: bool,
81 pub temporal_id: u8,
83 pub spatial_id: u8,
85}
86
87impl ObuHeader {
88 #[allow(clippy::cast_possible_truncation)]
94 pub fn parse(reader: &mut BitReader<'_>) -> CodecResult<Self> {
95 let forbidden = reader.read_bit().map_err(CodecError::Core)?;
96 if forbidden != 0 {
97 return Err(CodecError::InvalidBitstream(
98 "OBU forbidden bit is set".to_string(),
99 ));
100 }
101
102 let obu_type_raw = reader.read_bits(4).map_err(CodecError::Core)? as u8;
103 let obu_type = ObuType::from(obu_type_raw);
104 let has_extension = reader.read_bit().map_err(CodecError::Core)? != 0;
105 let has_size = reader.read_bit().map_err(CodecError::Core)? != 0;
106
107 let reserved = reader.read_bit().map_err(CodecError::Core)?;
108 if reserved != 0 {
109 return Err(CodecError::InvalidBitstream(
110 "OBU reserved bit is set".to_string(),
111 ));
112 }
113
114 let (temporal_id, spatial_id) = if has_extension {
115 let temporal_id = reader.read_bits(3).map_err(CodecError::Core)? as u8;
116 let spatial_id = reader.read_bits(2).map_err(CodecError::Core)? as u8;
117 let _reserved = reader.read_bits(3).map_err(CodecError::Core)?;
118 (temporal_id, spatial_id)
119 } else {
120 (0, 0)
121 };
122
123 Ok(Self {
124 obu_type,
125 has_extension,
126 has_size,
127 temporal_id,
128 spatial_id,
129 })
130 }
131
132 #[must_use]
134 pub const fn header_size(&self) -> usize {
135 if self.has_extension {
136 2
137 } else {
138 1
139 }
140 }
141
142 #[must_use]
144 pub fn to_bytes(&self) -> Vec<u8> {
145 let mut bytes = Vec::with_capacity(2);
146 let obu_type_val: u8 = self.obu_type.into();
147 let first_byte = (obu_type_val << 3)
148 | (u8::from(self.has_extension) << 2)
149 | (u8::from(self.has_size) << 1);
150 bytes.push(first_byte);
151
152 if self.has_extension {
153 let ext_byte = (self.temporal_id << 5) | (self.spatial_id << 3);
154 bytes.push(ext_byte);
155 }
156
157 bytes
158 }
159}
160
161#[allow(clippy::cast_possible_truncation)]
163pub fn parse_leb128(reader: &mut BitReader<'_>) -> CodecResult<u64> {
164 let mut value: u64 = 0;
165 let mut shift = 0;
166
167 loop {
168 let byte = reader.read_bits(8).map_err(CodecError::Core)? as u8;
169 value |= u64::from(byte & 0x7F) << shift;
170
171 if byte & 0x80 == 0 {
172 break;
173 }
174
175 shift += 7;
176 if shift >= 64 {
177 return Err(CodecError::InvalidBitstream(
178 "LEB128 value overflow".to_string(),
179 ));
180 }
181 }
182
183 Ok(value)
184}
185
186#[must_use]
188pub fn encode_leb128(mut value: u64) -> Vec<u8> {
189 let mut bytes = Vec::new();
190
191 loop {
192 let mut byte = (value & 0x7F) as u8;
193 value >>= 7;
194
195 if value != 0 {
196 byte |= 0x80;
197 }
198 bytes.push(byte);
199
200 if value == 0 {
201 break;
202 }
203 }
204
205 bytes
206}
207
208#[allow(clippy::cast_possible_truncation)]
210pub fn parse_obu(data: &[u8]) -> CodecResult<(ObuHeader, &[u8], usize)> {
211 let mut reader = BitReader::new(data);
212 let header = ObuHeader::parse(&mut reader)?;
213
214 let size = if header.has_size {
215 parse_leb128(&mut reader)? as usize
216 } else {
217 let header_bytes = reader.bits_read().div_ceil(8);
218 data.len().saturating_sub(header_bytes)
219 };
220
221 let header_bytes = reader.bits_read().div_ceil(8);
222 let total_size = header_bytes + size;
223
224 if total_size > data.len() {
225 return Err(CodecError::InvalidBitstream(format!(
226 "OBU size {} exceeds available data {}",
227 total_size,
228 data.len()
229 )));
230 }
231
232 let payload = &data[header_bytes..header_bytes + size];
233 Ok((header, payload, total_size))
234}
235
236pub struct ObuIterator<'a> {
238 data: &'a [u8],
239 offset: usize,
240}
241
242impl<'a> ObuIterator<'a> {
243 #[must_use]
245 pub const fn new(data: &'a [u8]) -> Self {
246 Self { data, offset: 0 }
247 }
248}
249
250impl<'a> Iterator for ObuIterator<'a> {
251 type Item = CodecResult<(ObuHeader, &'a [u8])>;
252
253 fn next(&mut self) -> Option<Self::Item> {
254 if self.offset >= self.data.len() {
255 return None;
256 }
257
258 match parse_obu(&self.data[self.offset..]) {
259 Ok((header, payload, total_size)) => {
260 self.offset += total_size;
261 Some(Ok((header, payload)))
262 }
263 Err(e) => Some(Err(e)),
264 }
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_obu_type_from_u8() {
274 assert_eq!(ObuType::from(1), ObuType::SequenceHeader);
275 assert_eq!(ObuType::from(6), ObuType::Frame);
276 }
277
278 #[test]
279 fn test_leb128_single_byte() {
280 let data = [0x7F];
281 let mut reader = BitReader::new(&data);
282 assert_eq!(parse_leb128(&mut reader).expect("should succeed"), 127);
283 }
284
285 #[test]
286 fn test_leb128_multi_byte() {
287 let data = [0x80, 0x01];
288 let mut reader = BitReader::new(&data);
289 assert_eq!(parse_leb128(&mut reader).expect("should succeed"), 128);
290 }
291
292 #[test]
293 fn test_encode_leb128() {
294 assert_eq!(encode_leb128(0), vec![0x00]);
295 assert_eq!(encode_leb128(127), vec![0x7F]);
296 assert_eq!(encode_leb128(128), vec![0x80, 0x01]);
297 }
298}