Skip to main content

iris/video/
mod.rs

1pub mod frame;
2pub mod iterator;
3pub mod metadata;
4pub mod reader;
5pub mod writer;
6
7pub use frame::Frame;
8pub use iterator::{FrameExt, FrameIterator, load_animated_image, load_image_sequence};
9pub use metadata::{ContainerFormat, PixelFormat, StreamInfo, StreamType, VideoMetadata};
10pub use reader::{SeekMode, VideoOpenOptions, VideoReader};
11pub use writer::{OutputFormat, VideoWriteOptions, VideoWriter};
12
13use crate::error::Result;
14use crate::image::Image;
15use burn::tensor::backend::Backend;
16use std::path::Path;
17
18/// Legacy video capture with mock frame generation.
19///
20/// For real video file reading, use [`VideoReader`] instead.
21pub struct VideoCapture<B: Backend> {
22    pub source_path: String,
23    #[allow(dead_code)]
24    device: B::Device,
25    current_frame: usize,
26    total_frames: usize,
27}
28
29impl<B: Backend> VideoCapture<B> {
30    /// Opens a video file or stream for reading.
31    pub fn open(path: impl AsRef<Path>, device: &B::Device) -> Result<Self> {
32        let path_str = path.as_ref().to_string_lossy().into_owned();
33        Ok(Self {
34            source_path: path_str,
35            device: device.clone(),
36            current_frame: 0,
37            total_frames: 100,
38        })
39    }
40
41    /// Grabs and retrieves the next video frame.
42    /// Returns None when the video ends.
43    pub fn read(&mut self) -> Result<Option<Image<B>>> {
44        if self.current_frame >= self.total_frames {
45            return Ok(None);
46        }
47        self.current_frame += 1;
48
49        let w = 640;
50        let h = 480;
51        let mut flat_data = vec![0.0f32; 3 * h * w];
52
53        let frame_offset = (self.current_frame as f32) / (self.total_frames as f32);
54
55        for y in 0..h {
56            for x in 0..w {
57                flat_data[y * w + x] = (x as f32) / (w as f32);
58                flat_data[h * w + y * w + x] = (y as f32) / (h as f32);
59                flat_data[2 * h * w + y * w + x] = frame_offset;
60            }
61        }
62
63        let tensor_data = burn::tensor::TensorData::new(flat_data, [3, h, w]);
64        let tensor = burn::tensor::Tensor::<B, 3>::from_data(tensor_data, &self.device);
65        Ok(Some(Image::new(tensor)))
66    }
67}
68
69/// Legacy video writer with mock frame writing.
70///
71/// For real video file writing, use [`writer::VideoWriter`] instead.
72pub struct LegacyVideoWriter<B: Backend> {
73    pub dest_path: String,
74    #[allow(dead_code)]
75    width: usize,
76    #[allow(dead_code)]
77    height: usize,
78    #[allow(dead_code)]
79    fps: f64,
80    _marker: std::marker::PhantomData<B>,
81}
82
83impl<B: Backend> LegacyVideoWriter<B> {
84    /// Creates a video writer target.
85    pub fn create(path: impl AsRef<Path>, width: usize, height: usize, fps: f64) -> Result<Self> {
86        let path_str = path.as_ref().to_string_lossy().into_owned();
87        Ok(Self {
88            dest_path: path_str,
89            width,
90            height,
91            fps,
92            _marker: std::marker::PhantomData,
93        })
94    }
95
96    /// Writes a single frame to the video destination.
97    pub fn write(&mut self, _frame: &Image<B>) -> Result<()> {
98        Ok(())
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use crate::test_helpers::{TestBackend, test_device};
106
107    #[test]
108    fn test_video_capture_legacy() {
109        let device = test_device();
110        let mut capture = VideoCapture::<TestBackend>::open("mock_video.mp4", &device).unwrap();
111        assert_eq!(capture.source_path, "mock_video.mp4");
112
113        let frame = capture.read().unwrap();
114        assert!(frame.is_some());
115        let frame_img = frame.unwrap();
116        assert_eq!(frame_img.shape(), [3, 480, 640]);
117    }
118
119    #[test]
120    fn test_legacy_video_writer() {
121        let mut writer =
122            LegacyVideoWriter::<TestBackend>::create("output.mp4", 640, 480, 30.0).unwrap();
123        assert_eq!(writer.dest_path, "output.mp4");
124
125        let device = test_device();
126        let data = burn::tensor::TensorData::new(vec![0.5f32; 3 * 480 * 640], [3, 480, 640]);
127        let tensor = burn::tensor::Tensor::<TestBackend, 3>::from_data(data, &device);
128        let img = Image::new(tensor);
129        writer.write(&img).unwrap();
130    }
131
132    #[test]
133    fn test_frame_struct() {
134        let device = test_device();
135        let data = burn::tensor::TensorData::new(vec![0.5f32; 3 * 64 * 64], [3, 64, 64]);
136        let tensor = burn::tensor::Tensor::<TestBackend, 3>::from_data(data, &device);
137        let img = Image::new(tensor);
138
139        let frame = Frame::new(img, std::time::Duration::from_millis(33), 0);
140        assert_eq!(frame.width(), 64);
141        assert_eq!(frame.height(), 64);
142    }
143
144    #[test]
145    fn test_video_metadata_synthetic() {
146        let meta = VideoMetadata::synthetic(1920, 1080, 30.0, 300);
147        assert_eq!(meta.width, 1920);
148        assert_eq!(meta.height, 1080);
149        assert!((meta.fps - 30.0).abs() < 1e-6);
150        assert_eq!(meta.frame_count, 300);
151    }
152
153    #[test]
154    fn test_frame_iterator() {
155        let device = test_device();
156        let frames: Vec<Frame<TestBackend>> = (0..5)
157            .map(|i| {
158                let data = burn::tensor::TensorData::new(vec![0.5f32; 3 * 32 * 32], [3, 32, 32]);
159                let tensor = burn::tensor::Tensor::<TestBackend, 3>::from_data(data, &device);
160                let img = Image::new(tensor);
161                Frame::new(img, std::time::Duration::from_millis(i as u64 * 33), i)
162            })
163            .collect();
164
165        let mut iter = FrameIterator::new(frames);
166        assert_eq!(iter.total_frames(), 5);
167        assert!(iter.next().is_some());
168    }
169}