1use crate::{
17 error::NokhwaError,
18 pixel_format::FormatDecoder,
19 types::{FrameFormat, Resolution},
20};
21use bytes::Bytes;
22use image::ImageBuffer;
23use std::time::Duration;
24#[cfg(feature = "opencv-mat")]
25use opencv::{boxed_ref::BoxedRef, core::Mat};
26
27#[derive(Clone, Debug, Hash, PartialOrd, PartialEq, Eq)]
32pub struct Buffer {
33 resolution: Resolution,
34 buffer: Bytes,
35 source_frame_format: FrameFormat,
36 capture_timestamp: Option<Duration>,
37}
38
39impl Buffer {
40 #[must_use]
42 #[inline]
43 pub fn new(res: Resolution, buf: &[u8], source_frame_format: FrameFormat) -> Self {
44 Self {
45 resolution: res,
46 buffer: Bytes::copy_from_slice(buf),
47 source_frame_format,
48 capture_timestamp: None,
49 }
50 }
51
52 #[must_use]
54 #[inline]
55 pub fn with_timestamp(
56 res: Resolution,
57 buf: &[u8],
58 source_frame_format: FrameFormat,
59 capture_timestamp: Option<Duration>,
60 ) -> Self {
61 Self {
62 resolution: res,
63 buffer: Bytes::copy_from_slice(buf),
64 source_frame_format,
65 capture_timestamp,
66 }
67 }
68
69 #[must_use]
71 pub fn capture_timestamp(&self) -> Option<Duration> {
72 self.capture_timestamp
73 }
74
75 #[must_use]
77 pub fn resolution(&self) -> Resolution {
78 self.resolution
79 }
80
81 #[must_use]
83 pub fn buffer(&self) -> &[u8] {
84 &self.buffer
85 }
86
87 #[must_use]
89 pub fn buffer_bytes(&self) -> Bytes {
90 self.buffer.clone()
91 }
92
93 #[must_use]
95 pub fn source_frame_format(&self) -> FrameFormat {
96 self.source_frame_format
97 }
98
99 #[inline]
103 pub fn decode_image<F: FormatDecoder>(
104 &self,
105 ) -> Result<ImageBuffer<F::Output, Vec<u8>>, NokhwaError> {
106 let new_data = F::write_output(self.source_frame_format, self.resolution, &self.buffer)?;
107 let image =
108 ImageBuffer::from_raw(self.resolution.width_x, self.resolution.height_y, new_data)
109 .ok_or(NokhwaError::ProcessFrameError {
110 src: self.source_frame_format,
111 destination: stringify!(F).to_string(),
112 error: "Failed to create buffer".to_string(),
113 })?;
114 Ok(image)
115 }
116
117 #[inline]
121 pub fn decode_image_to_buffer<F: FormatDecoder>(
122 &self,
123 buffer: &mut [u8],
124 ) -> Result<(), NokhwaError> {
125 F::write_output_buffer(
126 self.source_frame_format,
127 self.resolution,
128 &self.buffer,
129 buffer,
130 )
131 }
132
133 #[cfg(feature = "opencv-mat")]
145 #[cfg_attr(feature = "docs-features", doc(cfg(feature = "opencv-mat")))]
146 pub fn decode_opencv_mat<F: FormatDecoder>(
147 &mut self,
148 ) -> Result<BoxedRef<'_, Mat>, NokhwaError> {
149 use crate::buffer::channel_defs::make_mat;
150
151 make_mat::<F>(self.resolution, self.buffer())
152 }
153
154 #[cfg(feature = "opencv-mat")]
160 #[cfg_attr(feature = "docs-features", doc(cfg(feature = "opencv-mat")))]
161 #[allow(clippy::cast_possible_wrap)]
162 pub fn decode_into_opencv_mat<F: FormatDecoder>(
163 &mut self,
164 dst: &mut Mat,
165 ) -> Result<(), NokhwaError> {
166 use bytes::Buf;
167 use image::Pixel;
168 use opencv::core::{
169 Mat, MatTraitConst, MatTraitManual, Scalar, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4,
170 };
171
172 let array_type = match F::Output::CHANNEL_COUNT {
173 1 => CV_8UC1,
174 2 => CV_8UC2,
175 3 => CV_8UC3,
176 4 => CV_8UC4,
177 _ => {
178 return Err(NokhwaError::ProcessFrameError {
179 src: FrameFormat::RAWRGB,
180 destination: "OpenCV Mat".to_string(),
181 error: "Invalid Decoder FormatDecoder Channel Count".to_string(),
182 })
183 }
184 };
185
186 if dst.empty() {
188 *dst = Mat::new_rows_cols_with_default(
189 self.resolution.height_y as i32,
190 self.resolution.width_x as i32,
191 array_type,
192 Scalar::default(),
193 )
194 .map_err(|why| NokhwaError::ProcessFrameError {
195 src: FrameFormat::RAWRGB,
196 destination: "OpenCV Mat".to_string(),
197 error: why.to_string(),
198 })?;
199 } else {
200 if dst.typ() != array_type {
201 return Err(NokhwaError::ProcessFrameError {
202 src: FrameFormat::RAWRGB,
203 destination: "OpenCV Mat".to_string(),
204 error: "Invalid Matrix Channel Count".to_string(),
205 });
206 }
207
208 if dst.rows() != self.resolution.height_y as _
209 || dst.cols() != self.resolution.width_x as _
210 {
211 return Err(NokhwaError::ProcessFrameError {
212 src: FrameFormat::RAWRGB,
213 destination: "OpenCV Mat".to_string(),
214 error: "Invalid Matrix Dimensions".to_string(),
215 });
216 }
217 }
218
219 let mut bytes = match dst.data_bytes_mut() {
220 Ok(bytes) => bytes,
221 Err(_e) => {
222 return Err(NokhwaError::ProcessFrameError {
223 src: FrameFormat::RAWRGB,
224 destination: "OpenCV Mat".to_string(),
225 error: "Matrix Must Be Continuous".to_string(),
226 })
227 }
228 };
229
230 let mut buffer = self.buffer.as_ref();
231 if bytes.len() != buffer.len() {
232 return Err(NokhwaError::ProcessFrameError {
233 src: FrameFormat::RAWRGB,
234 destination: "OpenCV Mat".to_string(),
235 error: "Matrix Buffer Size Mismatch".to_string(),
236 });
237 }
238
239 buffer.copy_to_slice(&mut bytes);
240
241 Ok(())
242 }
243}
244
245#[cfg(feature = "opencv-mat")]
249pub mod channel_defs {
250 use crate::error::NokhwaError;
251 use crate::pixel_format::FormatDecoder;
252 use crate::types::{FrameFormat, Resolution};
253 use bytemuck::{cast_slice, Pod, Zeroable};
254 use image::Pixel;
255
256 #[cfg(feature = "opencv-mat")]
257 #[cfg_attr(feature = "docs-features", doc(cfg(feature = "opencv-mat")))]
258 pub(crate) fn make_mat<F>(
259 resolution: Resolution,
260 data: &[u8],
261 ) -> Result<opencv::boxed_ref::BoxedRef<'_, opencv::core::Mat>, NokhwaError>
262 where
263 F: FormatDecoder,
264 {
265 use crate::buffer::channel_defs::*;
266 use opencv::core::Mat;
267
268 let mat = match F::Output::CHANNEL_COUNT {
269 1 => Mat::new_rows_cols_with_data::<G8>(
270 resolution.width() as i32,
271 resolution.height() as i32,
272 cast_slice(data),
273 ),
274 2 => Mat::new_rows_cols_with_data::<GA8>(
275 resolution.width() as i32,
276 resolution.height() as i32,
277 cast_slice(data),
278 ),
279 3 => Mat::new_rows_cols_with_data::<RGB8>(
280 resolution.width() as i32,
281 resolution.height() as i32,
282 cast_slice(data),
283 ),
284 4 => Mat::new_rows_cols_with_data::<RGBA8>(
285 resolution.width() as i32,
286 resolution.height() as i32,
287 cast_slice(data),
288 ),
289 _ => {
290 return Err(NokhwaError::ProcessFrameError {
291 src: FrameFormat::RAWRGB,
292 destination: "OpenCV Mat".to_string(),
293 error: "Invalid Decoder FormatDecoder Channel Count".to_string(),
294 })
295 }
296 };
297
298 match mat {
299 Ok(m) => Ok(m),
300 Err(why) => Err(NokhwaError::ProcessFrameError {
301 src: FrameFormat::RAWRGB,
302 destination: "OpenCV Mat".to_string(),
303 error: why.to_string(),
304 }),
305 }
306 }
307
308 #[repr(transparent)]
310 #[derive(Copy, Clone, Debug)]
311 pub struct RGB8 {
312 pub data: [u8; 3],
313 }
314
315 unsafe impl opencv::core::DataType for RGB8 {
316 fn opencv_depth() -> i32 {
317 1
318 }
319
320 fn opencv_channels() -> i32 {
321 3
322 }
323 }
324
325 unsafe impl Pod for RGB8 {}
326
327 unsafe impl Zeroable for RGB8 {}
328
329 #[repr(transparent)]
331 #[derive(Copy, Clone, Debug)]
332 pub struct GA8 {
333 pub data: [u8; 2],
334 }
335
336 unsafe impl opencv::core::DataType for GA8 {
337 fn opencv_depth() -> i32 {
338 1
339 }
340
341 fn opencv_channels() -> i32 {
342 2
343 }
344 }
345
346 unsafe impl Zeroable for GA8 {}
347
348 unsafe impl Pod for GA8 {}
349
350 #[derive(Copy, Clone, Debug)]
352 pub struct G8 {
353 pub data: u8,
354 }
355
356 unsafe impl opencv::core::DataType for G8 {
357 fn opencv_depth() -> i32 {
358 1
359 }
360
361 fn opencv_channels() -> i32 {
362 1
363 }
364 }
365
366 unsafe impl Zeroable for G8 {}
367
368 unsafe impl Pod for G8 {}
369
370 #[repr(transparent)]
372 #[derive(Copy, Clone, Debug)]
373 pub struct RGBA8 {
374 pub data: [u8; 4],
375 }
376
377 unsafe impl opencv::core::DataType for RGBA8 {
378 fn opencv_depth() -> i32 {
379 1
380 }
381
382 fn opencv_channels() -> i32 {
383 4
384 }
385 }
386
387 unsafe impl Zeroable for RGBA8 {}
388
389 unsafe impl Pod for RGBA8 {}
390}