1pub mod buffer;
2pub mod obu;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct IvfHeader {
11 pub version: u16,
12 pub header_size: u16,
13 pub codec: [u8; 4],
14 pub width: u16,
15 pub height: u16,
16 pub timebase_denominator: u32,
17 pub timebase_numerator: u32,
18 pub num_frames: u32,
19 pub unused: u32,
20}
21
22impl IvfHeader {
23 pub fn codec_string(&self) -> &str {
24 std::str::from_utf8(&self.codec).unwrap_or("unknown")
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct IvfFrame<'a> {
30 pub index: usize,
31 pub pts: u64,
32 pub data: &'a [u8],
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum IvfError {
37 InvalidSignature,
38 TruncatedHeader,
39 InvalidHeaderSize(u16),
40 TruncatedFrameHeader {
41 frame_index: usize,
42 },
43 TruncatedFrameData {
44 frame_index: usize,
45 frame_size: usize,
46 remaining_bytes: usize,
47 },
48}
49
50impl std::fmt::Display for IvfError {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 match self {
53 IvfError::InvalidSignature => {
54 write!(f, "file does not start with IVF signature 'DKIF'")
55 }
56 IvfError::TruncatedHeader => write!(f, "IVF file must be at least 32 bytes"),
57 IvfError::InvalidHeaderSize(size) => write!(f, "invalid IVF header size: {size}"),
58 IvfError::TruncatedFrameHeader { frame_index } => {
59 write!(f, "truncated IVF frame header at frame {frame_index}")
60 }
61 IvfError::TruncatedFrameData {
62 frame_index,
63 frame_size,
64 remaining_bytes,
65 } => write!(
66 f,
67 "truncated IVF frame data at frame {frame_index}: expected {frame_size} bytes, only {remaining_bytes} remain"
68 ),
69 }
70 }
71}
72
73impl std::error::Error for IvfError {}
74
75pub struct IvfReader<'a> {
76 data: &'a [u8],
77 header: IvfHeader,
78}
79
80impl<'a> IvfReader<'a> {
81 pub fn new(data: &'a [u8]) -> Result<Self, IvfError> {
82 if data.len() < 32 {
83 return Err(IvfError::TruncatedHeader);
84 }
85 if !data.starts_with(b"DKIF") {
86 return Err(IvfError::InvalidSignature);
87 }
88
89 let header_size = u16::from_le_bytes([data[6], data[7]]);
90 if header_size < 32 || header_size as usize > data.len() {
91 return Err(IvfError::InvalidHeaderSize(header_size));
92 }
93
94 let header = IvfHeader {
95 version: u16::from_le_bytes([data[4], data[5]]),
96 header_size,
97 codec: [data[8], data[9], data[10], data[11]],
98 width: u16::from_le_bytes([data[12], data[13]]),
99 height: u16::from_le_bytes([data[14], data[15]]),
100 timebase_denominator: u32::from_le_bytes([data[16], data[17], data[18], data[19]]),
101 timebase_numerator: u32::from_le_bytes([data[20], data[21], data[22], data[23]]),
102 num_frames: u32::from_le_bytes([data[24], data[25], data[26], data[27]]),
103 unused: u32::from_le_bytes([data[28], data[29], data[30], data[31]]),
104 };
105
106 Ok(Self { data, header })
107 }
108
109 pub fn header(&self) -> &IvfHeader {
110 &self.header
111 }
112
113 pub fn frames(&self) -> IvfFrames<'a> {
114 IvfFrames {
115 data: self.data,
116 offset: self.header.header_size as usize,
117 frame_index: 0,
118 failed: false,
119 }
120 }
121}
122
123pub struct IvfFrames<'a> {
124 data: &'a [u8],
125 offset: usize,
126 frame_index: usize,
127 failed: bool,
128}
129
130impl<'a> Iterator for IvfFrames<'a> {
131 type Item = Result<IvfFrame<'a>, IvfError>;
132
133 fn next(&mut self) -> Option<Self::Item> {
134 if self.failed || self.offset >= self.data.len() {
135 return None;
136 }
137
138 if self.offset + 12 > self.data.len() {
139 self.failed = true;
140 return Some(Err(IvfError::TruncatedFrameHeader {
141 frame_index: self.frame_index,
142 }));
143 }
144
145 let frame_size = u32::from_le_bytes([
146 self.data[self.offset],
147 self.data[self.offset + 1],
148 self.data[self.offset + 2],
149 self.data[self.offset + 3],
150 ]) as usize;
151
152 let pts = u64::from_le_bytes([
153 self.data[self.offset + 4],
154 self.data[self.offset + 5],
155 self.data[self.offset + 6],
156 self.data[self.offset + 7],
157 self.data[self.offset + 8],
158 self.data[self.offset + 9],
159 self.data[self.offset + 10],
160 self.data[self.offset + 11],
161 ]);
162
163 let data_offset = self.offset + 12;
164
165 if data_offset + frame_size > self.data.len() {
166 self.failed = true;
167 return Some(Err(IvfError::TruncatedFrameData {
168 frame_index: self.frame_index,
169 frame_size,
170 remaining_bytes: self.data.len().saturating_sub(data_offset),
171 }));
172 }
173
174 let frame = IvfFrame {
175 index: self.frame_index,
176 pts,
177 data: &self.data[data_offset..data_offset + frame_size],
178 };
179
180 self.offset = data_offset + frame_size;
181 self.frame_index += 1;
182 Some(Ok(frame))
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 fn build_ivf(frame_payloads: &[&[u8]]) -> Vec<u8> {
191 let mut data = Vec::new();
192 data.extend_from_slice(b"DKIF");
193 data.extend_from_slice(&0u16.to_le_bytes());
194 data.extend_from_slice(&32u16.to_le_bytes());
195 data.extend_from_slice(b"AV01");
196 data.extend_from_slice(&640u16.to_le_bytes());
197 data.extend_from_slice(&480u16.to_le_bytes());
198 data.extend_from_slice(&30u32.to_le_bytes());
199 data.extend_from_slice(&1u32.to_le_bytes());
200 data.extend_from_slice(&(frame_payloads.len() as u32).to_le_bytes());
201 data.extend_from_slice(&0u32.to_le_bytes());
202
203 for (index, payload) in frame_payloads.iter().enumerate() {
204 data.extend_from_slice(&(payload.len() as u32).to_le_bytes());
205 data.extend_from_slice(&(index as u64).to_le_bytes());
206 data.extend_from_slice(payload);
207 }
208
209 data
210 }
211
212 #[test]
213 fn test_parse_ivf_header() {
214 let data = build_ivf(&[b"\x12\x00"]);
215 let ivf = IvfReader::new(&data).expect("valid IVF header");
216
217 assert_eq!(ivf.header().codec, *b"AV01");
218 assert_eq!(ivf.header().codec_string(), "AV01");
219 assert_eq!(ivf.header().width, 640);
220 assert_eq!(ivf.header().height, 480);
221 assert_eq!(ivf.header().num_frames, 1);
222 }
223
224 #[test]
225 fn test_iterate_ivf_frames() {
226 let data = build_ivf(&[b"\x12\x00", b"\x0A\x01\x02"]);
227 let ivf = IvfReader::new(&data).expect("valid IVF data");
228 let frames = ivf
229 .frames()
230 .collect::<Result<Vec<_>, _>>()
231 .expect("valid IVF frames");
232
233 assert_eq!(frames.len(), 2);
234 assert_eq!(frames[0].index, 0);
235 assert_eq!(frames[0].pts, 0);
236 assert_eq!(frames[0].data, b"\x12\x00");
237 assert_eq!(frames[1].index, 1);
238 assert_eq!(frames[1].pts, 1);
239 assert_eq!(frames[1].data, b"\x0A\x01\x02");
240 }
241}