Skip to main content

iris/video/
frame.rs

1use crate::image::Image;
2use burn::tensor::backend::Backend;
3use std::time::Duration;
4
5/// A single video frame with timing metadata.
6///
7/// Wraps an `Image` with presentation timestamp and duration information
8/// for proper video playback and editing workflows.
9#[derive(Clone, Debug)]
10pub struct Frame<B: Backend> {
11    /// The image data for this frame (C, H, W tensor).
12    pub image: Image<B>,
13    /// Presentation timestamp — when this frame should be displayed.
14    pub pts: Duration,
15    /// How long this frame should be displayed (used for variable frame rate).
16    pub duration: Duration,
17    /// Frame index in the sequence (0-based).
18    pub index: usize,
19    /// Whether this frame is a keyframe (I-frame).
20    pub is_keyframe: bool,
21}
22
23impl<B: Backend> Frame<B> {
24    /// Creates a new frame with the given image and timestamp.
25    #[must_use]
26    pub fn new(image: Image<B>, pts: Duration, index: usize) -> Self {
27        Self {
28            image,
29            pts,
30            duration: Duration::ZERO,
31            index,
32            is_keyframe: false,
33        }
34    }
35
36    /// Creates a keyframe (I-frame) at the given timestamp.
37    #[must_use]
38    pub fn keyframe(image: Image<B>, pts: Duration, index: usize) -> Self {
39        Self {
40            image,
41            pts,
42            duration: Duration::ZERO,
43            index,
44            is_keyframe: true,
45        }
46    }
47
48    /// Sets the display duration for this frame (for variable frame rate).
49    #[must_use]
50    pub fn with_duration(mut self, duration: Duration) -> Self {
51        self.duration = duration;
52        self
53    }
54
55    /// Returns the width of the frame in pixels.
56    #[must_use]
57    pub fn width(&self) -> usize {
58        self.image.width()
59    }
60
61    /// Returns the height of the frame in pixels.
62    #[must_use]
63    pub fn height(&self) -> usize {
64        self.image.height()
65    }
66
67    /// Returns the number of color channels.
68    #[must_use]
69    pub fn channels(&self) -> usize {
70        self.image.channels()
71    }
72
73    /// Returns the frame shape as [C, H, W].
74    #[must_use]
75    pub fn shape(&self) -> [usize; 3] {
76        self.image.shape()
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use crate::test_helpers::{TestBackend, test_device};
84    use burn::tensor::{Tensor, TensorData};
85
86    #[test]
87    fn test_frame_creation() {
88        let device = test_device();
89        let data = TensorData::new(vec![0.5f32; 3 * 64 * 64], [3, 64, 64]);
90        let tensor = Tensor::<TestBackend, 3>::from_data(data, &device);
91        let img = Image::new(tensor);
92
93        let frame = Frame::new(img, Duration::from_millis(33), 0);
94        assert_eq!(frame.width(), 64);
95        assert_eq!(frame.height(), 64);
96        assert_eq!(frame.channels(), 3);
97        assert_eq!(frame.index, 0);
98        assert!(!frame.is_keyframe);
99
100        let kf = Frame::keyframe(frame.image.clone(), Duration::from_millis(0), 0);
101        assert!(kf.is_keyframe);
102    }
103
104    #[test]
105    fn test_frame_with_duration() {
106        let device = test_device();
107        let data = TensorData::new(vec![0.5f32; 3 * 32 * 32], [3, 32, 32]);
108        let tensor = Tensor::<TestBackend, 3>::from_data(data, &device);
109        let img = Image::new(tensor);
110
111        let frame =
112            Frame::new(img, Duration::from_millis(0), 0).with_duration(Duration::from_millis(33));
113        assert_eq!(frame.duration, Duration::from_millis(33));
114    }
115}