From 31b60ae28e6aff6d23b378cd77e288c96c7db148 Mon Sep 17 00:00:00 2001 From: dyknon Date: Sun, 23 Feb 2025 08:33:02 +0900 Subject: abstruction --- src/v4l2cairo.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/v4l2cairo.rs (limited to 'src/v4l2cairo.rs') diff --git a/src/v4l2cairo.rs b/src/v4l2cairo.rs new file mode 100644 index 0000000..322a14b --- /dev/null +++ b/src/v4l2cairo.rs @@ -0,0 +1,88 @@ +use anyhow::{anyhow, Result}; +use crate::gtk; +use crate::v4l2; +use crate::color::yuv2rgb; +use zune_jpeg::JpegDecoder as JpegDec; +use zune_jpeg::zune_core::options::DecoderOptions as JpegOptions; +use zune_jpeg::zune_core::colorspace::ColorSpace as JpegColorSpace; + +pub struct V4l2Cairo(v4l2::CaptStream); +impl V4l2Cairo{ + pub fn new(inner: v4l2::CaptStream) -> Self{ + V4l2Cairo(inner) + } +} +impl gtk::Source for V4l2Cairo{ + type Attr = (); + fn next(&mut self, fbpool: impl gtk::FbSourceOnce) + -> Result>{ + let mut fbpool = Some(fbpool); + let (w, h) = (self.0.width(), self.0.height()); + let s = self.0.bytesperline(); + let pixelformat = self.0.pixelformat(); + loop{ + let img = self.0.next(|frame, _|{ + if &pixelformat == "YUYV"{ + if w % 2 != 0{ + return Err(anyhow!("invalid width of YUYV")); + } + if frame.len() < w*h*2{ + return Err(anyhow!("invalid size of YUYV")); + } + let mut img = fbpool.take().unwrap().get(w, h)?; + let stride: usize = img.stride().try_into()?; + let mut imgslice = img.data()?; + for (x, y) in (0..h).map( + |y| (0..w).map(move |x|(x, y))).flatten(){ + let p = s*y + x*2; + let (r, g, b) = yuv2rgb( + frame[p], frame[p/4*4 + 1], frame[p/4*4 + 3]); + imgslice[stride*y + x*4 + 0] = b; + imgslice[stride*y + x*4 + 1] = g; + imgslice[stride*y + x*4 + 2] = r; + imgslice[stride*y + x*4 + 3] = 0; + } + drop(imgslice); + Ok(img) + }else if &pixelformat == "MJPG" || &pixelformat == "JPEG"{ + // Jpeg is not placed in start of slice in some situation. + // It is even possible that there are no Jpeg data. + let jindex = (0..frame.len()-1) + .filter(|i| frame[*i] == 0xff && frame[i+1] == 0xd8) + .next() + .ok_or(anyhow!("jpeg not found"))?; + + let mut jpeg = JpegDec::new_with_options( + &frame[jindex..], + JpegOptions::new_fast() + .jpeg_set_out_colorspace(JpegColorSpace::BGRA)); + let b = jpeg.decode()?; + let info = jpeg.info().unwrap(); + + if info.width as usize != w || info.height as usize != h{ + return Err(anyhow!("invalid size of jpeg")); + } + let mut img = fbpool.take().unwrap().get(w, h)?; + let stride: usize = img.stride().try_into()?; + let mut imgslice = img.data()?; + for y in 0..h{ + imgslice[stride*y..stride*y+w*4] + .copy_from_slice(&b[y*w*4..((y+1)*w)*4]); + } + drop(imgslice); + Ok(img) + }else{ + unimplemented!() + } + })?; + + if let Ok(img) = img{ + return Ok(gtk::Packet{ + image: img.take_data()?, + attr: (), + }); + } + } + } +} + -- cgit v1.2.3