1use crate::error::{CodecError, CodecResult};
7use crate::frame::{FrameType, Plane, VideoFrame};
8use crate::traits::VideoDecoder;
9use oximedia_core::{CodecId, PixelFormat, Rational, Timestamp};
10
11use super::crc32::crc32_mpeg2;
12use super::prediction::{decode_line, predict_median};
13use super::range_coder::SimpleRangeDecoder;
14use super::types::{
15 Ffv1ChromaType, Ffv1Colorspace, Ffv1Config, Ffv1Version, SliceHeader, CONTEXT_COUNT,
16 INITIAL_STATE,
17};
18
19pub struct Ffv1Decoder {
37 config: Option<Ffv1Config>,
39 output_queue: Vec<VideoFrame>,
41 flushing: bool,
43 frame_count: u64,
45 plane_states: Vec<Vec<u8>>,
47}
48
49impl Ffv1Decoder {
50 pub fn new() -> Self {
52 Self {
53 config: None,
54 output_queue: Vec::new(),
55 flushing: false,
56 frame_count: 0,
57 plane_states: Vec::new(),
58 }
59 }
60
61 pub fn with_extradata(extradata: &[u8]) -> CodecResult<Self> {
63 let mut dec = Self::new();
64 dec.parse_config(extradata)?;
65 Ok(dec)
66 }
67
68 fn parse_config(&mut self, data: &[u8]) -> CodecResult<()> {
74 if data.len() < 13 {
79 return Err(CodecError::InvalidBitstream(format!(
80 "FFV1 config too short: {} bytes, need at least 13",
81 data.len()
82 )));
83 }
84
85 let version = Ffv1Version::from_u8(data[0])?;
86 let colorspace = Ffv1Colorspace::from_u8(data[1])?;
87 let h_shift = u32::from(data[2]);
88 let v_shift = u32::from(data[3]);
89 let chroma_type = Ffv1ChromaType::from_shifts(h_shift, v_shift)?;
90 let bits_per_raw_sample = data[4];
91 let ec = data[5] != 0;
92 let num_h_slices = u32::from(data[6]);
93 let num_v_slices = u32::from(data[7]);
94
95 let width_bytes: [u8; 4] = data[8..12]
97 .try_into()
98 .map_err(|_| CodecError::InvalidBitstream("bad width bytes".to_string()))?;
99 let height_bytes_end = 12 + 4;
100 if data.len() < height_bytes_end {
101 return Err(CodecError::InvalidBitstream(
102 "FFV1 config truncated".to_string(),
103 ));
104 }
105 let height_bytes: [u8; 4] = data[12..16]
106 .try_into()
107 .map_err(|_| CodecError::InvalidBitstream("bad height bytes".to_string()))?;
108
109 let width = u32::from_le_bytes(width_bytes);
110 let height = u32::from_le_bytes(height_bytes);
111
112 let config = Ffv1Config {
113 version,
114 width,
115 height,
116 colorspace,
117 chroma_type,
118 bits_per_raw_sample,
119 num_h_slices,
120 num_v_slices,
121 ec,
122 range_coder_mode: version.uses_range_coder(),
123 state_transition_delta: Vec::new(),
124 };
125 config.validate()?;
126
127 self.init_states(&config);
128 self.config = Some(config);
129 Ok(())
130 }
131
132 fn init_states(&mut self, config: &Ffv1Config) {
134 let plane_count = config.plane_count();
135 self.plane_states.clear();
136 for _ in 0..plane_count {
137 self.plane_states.push(vec![INITIAL_STATE; CONTEXT_COUNT]);
138 }
139 }
140
141 fn reset_states(&mut self) {
143 for states in &mut self.plane_states {
144 for s in states.iter_mut() {
145 *s = INITIAL_STATE;
146 }
147 }
148 }
149
150 fn decode_frame(&mut self, data: &[u8], pts: i64) -> CodecResult<VideoFrame> {
152 let config = self
154 .config
155 .as_ref()
156 .ok_or_else(|| CodecError::DecoderError("FFV1 decoder not configured".to_string()))?;
157
158 let width = config.width;
159 let height = config.height;
160 let plane_count = config.plane_count();
161 let ec = config.ec;
162 let num_slices = config.num_slices();
163 let num_h_slices = config.num_h_slices;
164 let num_v_slices = config.num_v_slices;
165 let max_val = config.max_sample_value();
166
167 let plane_dims: Vec<(u32, u32)> = (0..plane_count)
168 .map(|i| config.plane_dimensions(i))
169 .collect();
170
171 let pixel_format = match (
173 config.colorspace,
174 config.chroma_type,
175 config.bits_per_raw_sample,
176 ) {
177 (Ffv1Colorspace::YCbCr, Ffv1ChromaType::Chroma420, 8) => PixelFormat::Yuv420p,
178 (Ffv1Colorspace::YCbCr, Ffv1ChromaType::Chroma422, 8) => PixelFormat::Yuv422p,
179 (Ffv1Colorspace::YCbCr, Ffv1ChromaType::Chroma444, 8) => PixelFormat::Yuv444p,
180 _ => PixelFormat::Yuv420p, };
182
183 let _ = config;
185
186 let is_keyframe = self.frame_count == 0;
187
188 if is_keyframe {
189 self.reset_states();
190 }
191
192 let mut frame = VideoFrame::new(pixel_format, width, height);
193 frame.timestamp = Timestamp::new(pts, Rational::new(1, 1000));
194 frame.frame_type = if is_keyframe {
195 FrameType::Key
196 } else {
197 FrameType::Inter
198 };
199
200 let mut planes_data: Vec<Vec<Vec<i32>>> = Vec::with_capacity(plane_count);
202
203 if num_slices == 1 {
204 let slice_data = if ec && data.len() >= 4 {
206 let payload = &data[..data.len() - 4];
208 let stored_crc_bytes: [u8; 4] = data[data.len() - 4..]
209 .try_into()
210 .map_err(|_| CodecError::InvalidBitstream("bad CRC bytes".to_string()))?;
211 let stored_crc = u32::from_le_bytes(stored_crc_bytes);
212 let computed_crc = crc32_mpeg2(payload);
213 if stored_crc != computed_crc {
214 return Err(CodecError::InvalidBitstream(format!(
215 "FFV1 slice CRC mismatch: stored={stored_crc:#010X}, computed={computed_crc:#010X}"
216 )));
217 }
218 payload
219 } else {
220 data
221 };
222
223 for plane_idx in 0..plane_count {
224 let (pw, ph) = plane_dims[plane_idx];
225 let plane_header = SliceHeader {
226 slice_x: 0,
227 slice_y: 0,
228 slice_width: pw,
229 slice_height: ph,
230 };
231 let decoded = self.decode_slice(slice_data, &plane_header, plane_idx)?;
232 planes_data.push(decoded);
233 }
234 } else {
235 let slice_data_len = data.len() / (num_slices as usize);
237 for plane_idx in 0..plane_count {
238 let (pw, ph) = plane_dims[plane_idx];
239 let slice_h = ph / num_v_slices;
240 let slice_w = pw / num_h_slices;
241
242 let mut plane_lines: Vec<Vec<i32>> = Vec::new();
243
244 for sy in 0..num_v_slices {
245 for sx in 0..num_h_slices {
246 let slice_idx = (sy * num_h_slices + sx) as usize;
247 let start = slice_idx * slice_data_len;
248 let end = if slice_idx + 1 == num_slices as usize {
249 data.len()
250 } else {
251 start + slice_data_len
252 };
253 let segment = &data[start..end];
254
255 let sh = if sy == num_v_slices - 1 {
256 ph - sy * slice_h
257 } else {
258 slice_h
259 };
260 let sw = if sx == num_h_slices - 1 {
261 pw - sx * slice_w
262 } else {
263 slice_w
264 };
265
266 let header = SliceHeader {
267 slice_x: sx * slice_w,
268 slice_y: sy * slice_h,
269 slice_width: sw,
270 slice_height: sh,
271 };
272 let decoded = self.decode_slice(segment, &header, plane_idx)?;
273 for line in decoded {
274 plane_lines.push(line);
275 }
276 }
277 }
278 planes_data.push(plane_lines);
279 }
280 }
281
282 for (plane_idx, plane_lines) in planes_data.iter().enumerate() {
284 let (pw, ph) = plane_dims[plane_idx];
285 let stride = pw as usize;
286 let mut plane_data = vec![0u8; stride * ph as usize];
287
288 for (y, line) in plane_lines.iter().enumerate() {
289 if y >= ph as usize {
290 break;
291 }
292 let row_start = y * stride;
293 for (x, &sample) in line.iter().enumerate() {
294 if x >= pw as usize {
295 break;
296 }
297 let clamped = sample.clamp(0, max_val) as u8;
299 plane_data[row_start + x] = clamped;
300 }
301 }
302
303 frame
304 .planes
305 .push(Plane::with_dimensions(plane_data, stride, pw, ph));
306 }
307
308 self.frame_count += 1;
309 Ok(frame)
310 }
311
312 fn decode_slice(
316 &mut self,
317 data: &[u8],
318 header: &SliceHeader,
319 plane_idx: usize,
320 ) -> CodecResult<Vec<Vec<i32>>> {
321 let w = header.slice_width as usize;
322 let h = header.slice_height as usize;
323
324 if w == 0 || h == 0 {
325 return Ok(Vec::new());
326 }
327
328 if data.len() < 2 {
329 let mut lines = Vec::with_capacity(h);
331 for _ in 0..h {
332 lines.push(vec![0i32; w]);
333 }
334 return Ok(lines);
335 }
336
337 let mut decoder = SimpleRangeDecoder::new(data)?;
338 let states = self
339 .plane_states
340 .get_mut(plane_idx)
341 .ok_or_else(|| CodecError::Internal("invalid plane index".to_string()))?;
342
343 let mut lines: Vec<Vec<i32>> = Vec::with_capacity(h);
344 let mut prev_line = vec![0i32; w];
345
346 for _y in 0..h {
347 let mut line = Vec::with_capacity(w);
348 for x in 0..w {
349 let residual = decoder.get_symbol(states)?;
350 let left = if x > 0 { line[x - 1] } else { 0 };
351 let top = prev_line[x];
352 let top_left = if x > 0 { prev_line[x - 1] } else { 0 };
353 let pred = predict_median(left, top, top_left);
354 line.push(pred + residual);
355 }
356 prev_line.clone_from(&line);
357 lines.push(line);
358 }
359
360 Ok(lines)
361 }
362}
363
364impl VideoDecoder for Ffv1Decoder {
365 fn codec(&self) -> CodecId {
366 CodecId::Ffv1
367 }
368
369 fn send_packet(&mut self, data: &[u8], pts: i64) -> CodecResult<()> {
370 if self.flushing {
371 return Err(CodecError::DecoderError(
372 "decoder is flushing, cannot accept new packets".to_string(),
373 ));
374 }
375
376 if self.config.is_none() {
379 return Err(CodecError::DecoderError(
380 "FFV1 decoder not configured: call with_extradata() first".to_string(),
381 ));
382 }
383
384 let frame = self.decode_frame(data, pts)?;
385 self.output_queue.push(frame);
386 Ok(())
387 }
388
389 fn receive_frame(&mut self) -> CodecResult<Option<VideoFrame>> {
390 if self.output_queue.is_empty() {
391 Ok(None)
392 } else {
393 Ok(Some(self.output_queue.remove(0)))
394 }
395 }
396
397 fn flush(&mut self) -> CodecResult<()> {
398 self.flushing = true;
399 Ok(())
400 }
401
402 fn reset(&mut self) {
403 self.output_queue.clear();
404 self.flushing = false;
405 self.frame_count = 0;
406 self.reset_states();
407 }
408
409 fn output_format(&self) -> Option<PixelFormat> {
410 self.config
411 .as_ref()
412 .map(|c| match (c.colorspace, c.chroma_type) {
413 (Ffv1Colorspace::YCbCr, Ffv1ChromaType::Chroma420) => PixelFormat::Yuv420p,
414 (Ffv1Colorspace::YCbCr, Ffv1ChromaType::Chroma422) => PixelFormat::Yuv422p,
415 (Ffv1Colorspace::YCbCr, Ffv1ChromaType::Chroma444) => PixelFormat::Yuv444p,
416 _ => PixelFormat::Yuv420p,
417 })
418 }
419
420 fn dimensions(&self) -> Option<(u32, u32)> {
421 self.config.as_ref().map(|c| (c.width, c.height))
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428 use crate::traits::VideoDecoder;
429
430 fn make_config_bytes(width: u32, height: u32) -> Vec<u8> {
431 let mut data = Vec::new();
432 data.push(3); data.push(0); data.push(1); data.push(1); data.push(8); data.push(1); data.push(1); data.push(1); data.extend_from_slice(&width.to_le_bytes());
441 data.extend_from_slice(&height.to_le_bytes());
442 data
443 }
444
445 #[test]
446 #[ignore]
447 fn test_decoder_creation() {
448 let dec = Ffv1Decoder::new();
449 assert!(dec.config.is_none());
450 assert_eq!(dec.codec(), CodecId::Ffv1);
451 }
452
453 #[test]
454 #[ignore]
455 fn test_decoder_with_extradata() {
456 let config_data = make_config_bytes(320, 240);
457 let dec = Ffv1Decoder::with_extradata(&config_data).expect("valid config");
458 assert!(dec.config.is_some());
459 assert_eq!(dec.dimensions(), Some((320, 240)));
460 assert_eq!(dec.output_format(), Some(PixelFormat::Yuv420p));
461 }
462
463 #[test]
464 #[ignore]
465 fn test_decoder_invalid_config() {
466 assert!(Ffv1Decoder::with_extradata(&[0; 5]).is_err());
468 let mut bad = make_config_bytes(320, 240);
470 bad[0] = 99;
471 assert!(Ffv1Decoder::with_extradata(&bad).is_err());
472 }
473
474 #[test]
475 #[ignore]
476 fn test_decoder_not_configured() {
477 let mut dec = Ffv1Decoder::new();
478 assert!(dec.send_packet(&[0; 100], 0).is_err());
479 }
480
481 #[test]
482 #[ignore]
483 fn test_decoder_reset() {
484 let config_data = make_config_bytes(16, 16);
485 let mut dec = Ffv1Decoder::with_extradata(&config_data).expect("valid");
486 dec.frame_count = 10;
487 dec.flushing = true;
488 dec.reset();
489 assert_eq!(dec.frame_count, 0);
490 assert!(!dec.flushing);
491 }
492
493 #[test]
494 #[ignore]
495 fn test_decoder_flush() {
496 let config_data = make_config_bytes(16, 16);
497 let mut dec = Ffv1Decoder::with_extradata(&config_data).expect("valid");
498 dec.flush().expect("flush ok");
499 assert!(dec.flushing);
500 assert!(dec.send_packet(&[0; 100], 0).is_err());
502 }
503}