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
18pub 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 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 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
69pub 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 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 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}