use super::{CaptureBackend, CaptureConfig, CaptureError, FrameBuffer, PixelFormat};
pub struct FileBackend {
frames: Vec<FrameBuffer>,
current: usize,
is_open: bool,
loop_frames: bool,
width: u32,
height: u32,
}
impl FileBackend {
pub fn new(frames: Vec<FrameBuffer>) -> Self {
let (w, h) = frames.first().map_or((0, 0), |f| (f.width, f.height));
Self {
frames,
current: 0,
is_open: false,
loop_frames: true,
width: w,
height: h,
}
}
pub fn synthetic(width: u32, height: u32, value: u8) -> Self {
let data = vec![value; (width * height * 3) as usize];
let frame = FrameBuffer::new(data, width, height, PixelFormat::Rgb);
Self::new(vec![frame])
}
pub fn synthetic_sequence(width: u32, height: u32, num_frames: usize) -> Self {
let frames: Vec<_> = (0..num_frames)
.map(|i| {
let val = ((i * 20) % 256) as u8;
let data = vec![val; (width * height * 3) as usize];
let mut fb = FrameBuffer::new(data, width, height, PixelFormat::Rgb);
fb.timestamp_us = i as u64 * 33333; fb
})
.collect();
Self::new(frames)
}
pub fn set_loop(&mut self, loop_frames: bool) {
self.loop_frames = loop_frames;
}
pub fn num_frames(&self) -> usize {
self.frames.len()
}
}
impl CaptureBackend for FileBackend {
fn open(&mut self, _config: &CaptureConfig) -> Result<(), CaptureError> {
if self.frames.is_empty() {
return Err(CaptureError::DeviceNotFound("No frames loaded".into()));
}
self.is_open = true;
self.current = 0;
Ok(())
}
fn grab_frame(&mut self) -> Result<FrameBuffer, CaptureError> {
if !self.is_open {
return Err(CaptureError::NotOpen);
}
if self.current >= self.frames.len() {
if self.loop_frames {
self.current = 0;
} else {
return Err(CaptureError::CaptureError("No more frames".into()));
}
}
let frame = self.frames[self.current].clone();
self.current += 1;
Ok(frame)
}
fn is_open(&self) -> bool {
self.is_open
}
fn close(&mut self) {
self.is_open = false;
self.current = 0;
}
fn resolution(&self) -> (u32, u32) {
(self.width, self.height)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_file_backend_synthetic() {
let mut backend = FileBackend::synthetic(32, 32, 128);
assert_eq!(backend.num_frames(), 1);
assert!(!backend.is_open());
backend.open(&CaptureConfig::default()).unwrap();
assert!(backend.is_open());
let frame = backend.grab_frame().unwrap();
assert_eq!(frame.width, 32);
assert_eq!(frame.height, 32);
assert_eq!(frame.format, PixelFormat::Rgb);
assert_eq!(frame.data[0], 128);
}
#[test]
fn test_file_backend_loops() {
let mut backend = FileBackend::synthetic_sequence(8, 8, 3);
backend.open(&CaptureConfig::default()).unwrap();
let f1 = backend.grab_frame().unwrap();
let f2 = backend.grab_frame().unwrap();
let f3 = backend.grab_frame().unwrap();
assert_eq!(f1.data[0], 0);
assert_eq!(f2.data[0], 20);
assert_eq!(f3.data[0], 40);
let f4 = backend.grab_frame().unwrap();
assert_eq!(f4.data[0], 0);
}
#[test]
fn test_file_backend_no_loop() {
let mut backend = FileBackend::synthetic_sequence(8, 8, 2);
backend.set_loop(false);
backend.open(&CaptureConfig::default()).unwrap();
backend.grab_frame().unwrap();
backend.grab_frame().unwrap();
assert!(backend.grab_frame().is_err());
}
#[test]
fn test_file_backend_not_open() {
let mut backend = FileBackend::synthetic(8, 8, 0);
assert!(backend.grab_frame().is_err());
}
#[test]
fn test_file_backend_close_resets() {
let mut backend = FileBackend::synthetic_sequence(8, 8, 3);
backend.open(&CaptureConfig::default()).unwrap();
backend.grab_frame().unwrap();
backend.grab_frame().unwrap();
backend.close();
assert!(!backend.is_open());
backend.open(&CaptureConfig::default()).unwrap();
let f = backend.grab_frame().unwrap();
assert_eq!(f.data[0], 0); }
#[test]
fn test_file_backend_resolution() {
let backend = FileBackend::synthetic(320, 240, 0);
assert_eq!(backend.resolution(), (320, 240));
}
#[test]
fn test_file_backend_timestamps() {
let mut backend = FileBackend::synthetic_sequence(4, 4, 5);
backend.open(&CaptureConfig::default()).unwrap();
let f0 = backend.grab_frame().unwrap();
let f1 = backend.grab_frame().unwrap();
assert_eq!(f0.timestamp_us, 0);
assert_eq!(f1.timestamp_us, 33333);
}
}