From 31b60ae28e6aff6d23b378cd77e288c96c7db148 Mon Sep 17 00:00:00 2001 From: dyknon Date: Sun, 23 Feb 2025 08:33:02 +0900 Subject: abstruction --- src/main.rs | 206 +++--------------------------------------------------------- 1 file changed, 7 insertions(+), 199 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index ab54508..5e9ae13 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,56 +1,10 @@ -#![allow(unused)] - -use anyhow::{anyhow, Result}; -use std::sync::{Arc, Mutex}; -use std::future::poll_fn; -use std::task::{Context, Poll, Waker}; -use std::thread; - -use gtk4::{self as gtk, glib, cairo}; -use gtk4::prelude::*; -use glib::{clone, spawn_future_local}; - +use anyhow::Result; use sshcamera::v4l2::{Device as V4l2, Field}; -use sshcamera::color::yuv2rgb; -use jpeg_decoder::{self as jpeg, Decoder as JpegDec}; - -struct SignalChannel{ - waker: Option, - active: bool, -} -impl SignalChannel{ - fn new() -> Self{ - Self{ - waker: None, - active: false, - } - } - fn wake(&mut self){ - self.active = true; - if let Some(w) = self.waker.take(){ - w.wake(); - } - } - fn poll(this: &Arc>, ctx: &mut Context<'_>) -> Poll<()>{ - let mut l = this.lock().unwrap(); - if l.active{ - l.active = false; - Poll::Ready(()) - }else{ - l.waker = Some(ctx.waker().clone()); - Poll::Pending - } - } -} +use sshcamera::v4l2cairo::V4l2Cairo; +use sshcamera::gtk; +use gtk4::glib::ExitCode; -#[derive(Clone)] -struct AppState{ - frame_buf: Arc>>, - notify: Arc>, - fbpool: Arc>>, -} - -fn videothread(apps: AppState) -> Result<()>{ +fn main() -> Result{ let v = V4l2::open("/dev/video0")?; // TODO: It should be better. @@ -61,152 +15,6 @@ fn videothread(apps: AppState) -> Result<()>{ .build()?; assert!(["YUYV", "MJPG"].contains(&c.pixelformat().as_str())); assert!(c.field() == Field::None); - - let (w, h) = (c.width(), c.height()); - let s = c.bytesperline(); - loop{ - let img: Result = c.next(|frame, _|{ - let mut img = None; - let mut fbpool = apps.fbpool.lock().unwrap(); - while let Some(i) = fbpool.pop(){ - let i = i.into_inner(); - if i.width() as usize == w && i.height() as usize == h{ - img = Some(i); - break; - } - } - drop(fbpool); - let mut img = match img{ - Some(i) => i, - None => { - cairo::ImageSurface::create( - cairo::Format::Rgb24, - w.try_into()?, h.try_into()?)? - }, - }; - let stride: usize = img.stride().try_into()?; - let mut imgslice = img.data()?; - match c.pixelformat().as_str(){ - "YUYV" => { - 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) - }, - "MJPG" => { - // 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(&frame[jindex..]); - let b = jpeg.decode()?; - let info = jpeg.info().unwrap(); - - assert!((info.width as usize, info.height as usize) - == (w, h)); - for (x, y) in (0..h).map( - |y| (0..w).map(move |x|(x, y))).flatten(){ - imgslice[stride*y + x*4 + 0] = b[(y*w+x)*3 + 2]; - imgslice[stride*y + x*4 + 1] = b[(y*w+x)*3 + 1]; - imgslice[stride*y + x*4 + 2] = b[(y*w+x)*3 + 0]; - imgslice[stride*y + x*4 + 3] = 0; - } - - drop(imgslice); - Ok(img) - }, - _ => unreachable!(), - } - }).unwrap_or_else(|e| Err(e.into())); - - match img{ - Ok(img) => { - *apps.frame_buf.lock().unwrap() = Some(img.take_data().unwrap()); - apps.notify.lock().unwrap().wake(); - }, - Err(err) => { - println!("Skipping erroneous frame: {:?}", err); - }, - } - } -} - -fn gtkmain(app: >k::Application){ - let apps = AppState{ - frame_buf: Arc::new(Mutex::new(None)), - notify: Arc::new(Mutex::new(SignalChannel::new())), - fbpool: Arc::new(Mutex::new(Vec::new())), - }; - - thread::spawn(clone!{ - #[strong] apps, - move || videothread(apps).unwrap() - }); - - let draw = gtk::DrawingArea::new(); - let mut frame_cache: Option = None; - draw.set_draw_func(clone!{ - #[strong(rename_to=frame_buf)] apps.frame_buf, - #[strong(rename_to=fbpool)] apps.fbpool, - move |_draw, ctx, canvas_w, canvas_h|{ - ctx.set_source_rgb(0., 0., 0.); - ctx.paint().unwrap(); - - if let Some(newfb) = frame_buf.lock().unwrap().take(){ - if let Some(mut lastframe) = frame_cache.take(){ - let mut fbpool = fbpool.lock().unwrap(); - if fbpool.len() < 8{ - fbpool.push(lastframe.take_data().unwrap()); - } - } - frame_cache = Some(newfb.into_inner()); - } - if let Some(image) = frame_cache.clone(){ - let ipat = cairo::SurfacePattern::create(&image); - let scale = ((canvas_w as f64) / (image.width() as f64)).min( - (canvas_h as f64) / (image.height() as f64)); - ctx.scale(scale, scale); - ctx.set_source(&ipat).unwrap(); - ctx.paint().unwrap(); - } - } - }); - spawn_future_local(poll_fn(clone!{ - #[strong(rename_to=notify)] apps.notify, - #[strong] draw, - move |ctx|{ - loop{ - match SignalChannel::poll(¬ify, ctx){ - Poll::Ready(_) => { - draw.queue_draw(); - }, - pending => return pending, - } - } - } - })); - - let win = gtk::ApplicationWindow::builder() - .application(app) - .child(&draw) - .build(); - win.present(); -} - -fn main() -> Result{ - let app = gtk::Application::builder() - .build(); - app.connect_activate(gtkmain); - Ok(app.run()) + let v2c = V4l2Cairo::new(c); + gtk::main(v2c) } -- cgit v1.2.3