summaryrefslogtreecommitdiff
path: root/src/v4l2cairo.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/v4l2cairo.rs')
-rw-r--r--src/v4l2cairo.rs88
1 files changed, 88 insertions, 0 deletions
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<gtk::Packet<()>>{
+ 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: (),
+ });
+ }
+ }
+ }
+}
+