summaryrefslogtreecommitdiff
path: root/src/v4l2cairo.rs
blob: 322a14bd9bc56c90f0d263b54be9481f676e9529 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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: (),
                });
            }
        }
    }
}