otf_pixels_codec_avif/av1/
obu.rs1use super::bits::BitReader;
15use otf_pixels_core::{PixelsError, Result};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ObuType {
20 SequenceHeader,
22 TemporalDelimiter,
24 FrameHeader,
26 TileGroup,
28 Metadata,
30 Frame,
32 RedundantFrameHeader,
34 TileList,
36 Padding,
38 Reserved(u8),
41}
42
43impl ObuType {
44 fn from_bits(value: u32) -> Self {
45 match value {
46 1 => Self::SequenceHeader,
47 2 => Self::TemporalDelimiter,
48 3 => Self::FrameHeader,
49 4 => Self::TileGroup,
50 5 => Self::Metadata,
51 6 => Self::Frame,
52 7 => Self::RedundantFrameHeader,
53 8 => Self::TileList,
54 15 => Self::Padding,
55 other => Self::Reserved(other as u8),
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub struct ObuHeader {
63 pub kind: ObuType,
65 pub temporal_id: u8,
67 pub spatial_id: u8,
69}
70
71#[derive(Debug, Clone, Copy)]
73pub struct Obu<'a> {
74 pub header: ObuHeader,
76 pub payload: &'a [u8],
78 pub payload_start: usize,
81}
82
83impl<'a> Obu<'a> {
84 pub fn parse_stream(data: &'a [u8]) -> Result<Vec<Obu<'a>>> {
90 let mut obus = Vec::new();
91 let mut pos = 0;
92 while pos < data.len() {
93 let Some(rest) = data.get(pos..) else {
94 break;
95 };
96 let mut reader = BitReader::new(rest);
97
98 if reader.f(1)? != 0 {
99 return Err(PixelsError::malformed(
100 "avif",
101 "an AV1 OBU forbidden bit was set",
102 ));
103 }
104 let kind = ObuType::from_bits(reader.f(4)?);
105 let extension_flag = reader.flag()?;
106 let has_size_field = reader.flag()?;
107 let _obu_reserved_1bit = reader.f(1)?;
108
109 let (temporal_id, spatial_id) = if extension_flag {
110 let temporal_id = reader.f(3)? as u8;
111 let spatial_id = reader.f(2)? as u8;
112 let _extension_reserved_3bits = reader.f(3)?;
113 (temporal_id, spatial_id)
114 } else {
115 (0, 0)
116 };
117
118 let payload_len = if has_size_field {
119 usize::try_from(reader.leb128()?).map_err(|_| {
120 PixelsError::malformed("avif", "an AV1 OBU size exceeds this platform's usize")
121 })?
122 } else {
123 rest.len().saturating_sub(reader.byte_position())
126 };
127
128 let payload_start = pos + reader.byte_position();
129 let payload_end = payload_start
130 .checked_add(payload_len)
131 .filter(|&e| e <= data.len());
132 let Some(payload_end) = payload_end else {
133 return Err(PixelsError::malformed(
134 "avif",
135 "an AV1 OBU declares more bytes than the stream holds",
136 ));
137 };
138 let Some(payload) = data.get(payload_start..payload_end) else {
139 return Err(PixelsError::malformed(
140 "avif",
141 "an AV1 OBU payload range is invalid",
142 ));
143 };
144
145 obus.push(Obu {
146 header: ObuHeader {
147 kind,
148 temporal_id,
149 spatial_id,
150 },
151 payload,
152 payload_start,
153 });
154 pos = payload_end;
155 }
156 Ok(obus)
157 }
158}
159
160#[cfg(test)]
161#[allow(
162 clippy::unwrap_used,
163 clippy::indexing_slicing,
164 clippy::panic,
165 clippy::unusual_byte_groupings,
166 reason = "tests operate on known-good values and assert shapes directly"
167)]
168mod tests {
169 use super::*;
170 use otf_pixels_core::ErrorCode;
171
172 fn sized_obu(kind: u8, payload: &[u8]) -> Vec<u8> {
175 let mut out = Vec::new();
176 let header = (kind << 3) | 0b0000_0010;
178 out.push(header);
179 assert!(payload.len() < 128, "test payloads stay single-byte leb128");
180 out.push(payload.len() as u8);
181 out.extend_from_slice(payload);
182 out
183 }
184
185 #[test]
186 fn splits_a_stream_into_sized_obus() {
187 let mut stream = sized_obu(2, &[]); stream.extend(sized_obu(1, &[0xAA, 0xBB])); stream.extend(sized_obu(6, &[0xCC])); let obus = Obu::parse_stream(&stream).unwrap();
192 assert_eq!(obus.len(), 3);
193 assert_eq!(obus[0].header.kind, ObuType::TemporalDelimiter);
194 assert_eq!(obus[0].payload, &[] as &[u8]);
195 assert_eq!(obus[1].header.kind, ObuType::SequenceHeader);
196 assert_eq!(obus[1].payload, &[0xAA, 0xBB]);
197 assert_eq!(obus[2].header.kind, ObuType::Frame);
198 assert_eq!(obus[2].payload, &[0xCC]);
199 }
200
201 #[test]
202 fn reads_the_extension_header_ids() {
203 let mut stream = vec![0b0000_1110];
205 stream.push(0b011_10_000);
207 stream.push(0x01); stream.push(0x55); let obus = Obu::parse_stream(&stream).unwrap();
210 assert_eq!(obus.len(), 1);
211 assert_eq!(obus[0].header.temporal_id, 3);
212 assert_eq!(obus[0].header.spatial_id, 2);
213 assert_eq!(obus[0].payload, &[0x55]);
214 }
215
216 #[test]
217 fn a_sizeless_final_obu_takes_the_rest_of_the_buffer() {
218 let mut stream = vec![0b0011_0000];
220 stream.extend_from_slice(&[1, 2, 3, 4]);
221 let obus = Obu::parse_stream(&stream).unwrap();
222 assert_eq!(obus.len(), 1);
223 assert_eq!(obus[0].header.kind, ObuType::Frame);
224 assert_eq!(obus[0].payload, &[1, 2, 3, 4]);
225 }
226
227 #[test]
228 fn a_size_past_the_buffer_is_rejected() {
229 let stream = vec![0b0011_0010, 200, 1, 2, 3];
231 let err = Obu::parse_stream(&stream).unwrap_err();
232 assert_eq!(err.code(), ErrorCode::Malformed);
233 }
234
235 #[test]
236 fn a_set_forbidden_bit_is_rejected() {
237 let stream = vec![0b1011_0010, 0x00];
238 let err = Obu::parse_stream(&stream).unwrap_err();
239 assert_eq!(err.code(), ErrorCode::Malformed);
240 }
241
242 #[test]
243 fn an_unknown_type_is_carried_through_not_rejected() {
244 let stream = sized_obu(9, &[0x01]);
245 let obus = Obu::parse_stream(&stream).unwrap();
246 assert_eq!(obus[0].header.kind, ObuType::Reserved(9));
247 }
248}