1use bytes::Bytes;
2use yscv_tensor::Tensor;
3
4use crate::VideoError;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum PixelFormat {
8 GrayF32,
9 RgbF32,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct Rgb8Frame {
15 index: u64,
16 timestamp_us: u64,
17 width: usize,
18 height: usize,
19 data: Bytes,
20}
21
22impl Rgb8Frame {
23 pub fn new(
24 index: u64,
25 timestamp_us: u64,
26 width: usize,
27 height: usize,
28 data: Vec<u8>,
29 ) -> Result<Self, VideoError> {
30 Self::from_bytes(index, timestamp_us, width, height, data.into())
31 }
32
33 pub fn from_bytes(
34 index: u64,
35 timestamp_us: u64,
36 width: usize,
37 height: usize,
38 data: Bytes,
39 ) -> Result<Self, VideoError> {
40 let expected = width
41 .checked_mul(height)
42 .and_then(|pixels| pixels.checked_mul(3))
43 .ok_or_else(|| {
44 VideoError::Source(format!(
45 "raw frame dimensions overflow for width={width}, height={height}"
46 ))
47 })?;
48 if data.len() != expected {
49 return Err(VideoError::RawFrameSizeMismatch {
50 expected,
51 got: data.len(),
52 });
53 }
54 Ok(Self {
55 index,
56 timestamp_us,
57 width,
58 height,
59 data,
60 })
61 }
62
63 pub fn index(&self) -> u64 {
64 self.index
65 }
66
67 pub fn timestamp_us(&self) -> u64 {
68 self.timestamp_us
69 }
70
71 pub fn width(&self) -> usize {
72 self.width
73 }
74
75 pub fn height(&self) -> usize {
76 self.height
77 }
78
79 pub fn data(&self) -> &[u8] {
80 &self.data
81 }
82
83 pub fn into_data(self) -> Vec<u8> {
84 self.data.to_vec()
85 }
86
87 pub fn into_bytes(self) -> Bytes {
88 self.data
89 }
90}
91
92#[derive(Debug, Clone, PartialEq)]
94pub struct Frame {
95 index: u64,
96 timestamp_us: u64,
97 pixel_format: PixelFormat,
98 image: Tensor,
99}
100
101impl Frame {
102 pub fn new(index: u64, timestamp_us: u64, image: Tensor) -> Result<Self, VideoError> {
103 if image.rank() != 3 {
104 return Err(VideoError::InvalidFrameShape {
105 got: image.shape().to_vec(),
106 });
107 }
108 let channels = image.shape()[2];
109 let pixel_format = match channels {
110 1 => PixelFormat::GrayF32,
111 3 => PixelFormat::RgbF32,
112 _ => return Err(VideoError::UnsupportedChannelCount { channels }),
113 };
114 Ok(Self {
115 index,
116 timestamp_us,
117 pixel_format,
118 image,
119 })
120 }
121
122 pub fn index(&self) -> u64 {
123 self.index
124 }
125
126 pub fn timestamp_us(&self) -> u64 {
127 self.timestamp_us
128 }
129
130 pub fn pixel_format(&self) -> PixelFormat {
131 self.pixel_format
132 }
133
134 pub fn image(&self) -> &Tensor {
135 &self.image
136 }
137}