👕 refactor: extract Controller again 🥳
parent
d008baa34f
commit
fb5013ef3e
|
@ -23,6 +23,9 @@ impl Capture
|
||||||
{
|
{
|
||||||
let x = Device::open ("/dev/video0")?;
|
let x = Device::open ("/dev/video0")?;
|
||||||
|
|
||||||
|
// sudo apt install v4l-utils
|
||||||
|
// v4l2-ctl -d /dev/video0 --list-formats-ext
|
||||||
|
|
||||||
let x = x.video_capture (PixFormat::new (1280, 720, PixelFormat::MJPG))?;
|
let x = x.video_capture (PixFormat::new (1280, 720, PixelFormat::MJPG))?;
|
||||||
x.set_frame_interval(linuxvideo::Fract::new(1, 30)).unwrap ();
|
x.set_frame_interval(linuxvideo::Fract::new(1, 30)).unwrap ();
|
||||||
dbg! (x.format ());
|
dbg! (x.format ());
|
||||||
|
@ -72,4 +75,4 @@ pub enum Error
|
||||||
Io (#[from] std::io::Error),
|
Io (#[from] std::io::Error),
|
||||||
#[error ("output buffer too small")]
|
#[error ("output buffer too small")]
|
||||||
OutputBufferTooSmall,
|
OutputBufferTooSmall,
|
||||||
}
|
}
|
||||||
|
|
303
src/main.rs
303
src/main.rs
|
@ -10,7 +10,6 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use rayon::ThreadPool;
|
|
||||||
|
|
||||||
mod capture;
|
mod capture;
|
||||||
mod task;
|
mod task;
|
||||||
|
@ -22,95 +21,25 @@ fn main() -> Result <(), Error>
|
||||||
match args.next ().as_deref ()
|
match args.next ().as_deref ()
|
||||||
{
|
{
|
||||||
None => main_egui (),
|
None => main_egui (),
|
||||||
Some ("capture") =>
|
Some ("capture") =>
|
||||||
{
|
{
|
||||||
use std::io::Write;
|
let mut capture = capture::Capture::new ().unwrap ();
|
||||||
let mut cap = capture::Capture::new ()?;
|
let mut buf = vec! [0u8; capture.size_image ()];
|
||||||
let mut buf = vec! [0u8; cap.size_image ()];
|
|
||||||
let mut bytesused = 0;
|
|
||||||
|
|
||||||
for _ in 0..30
|
for _ in 0..10
|
||||||
{
|
{
|
||||||
cap.wait_for_frame (&mut buf).unwrap ();
|
capture.wait_for_frame (&mut buf).unwrap ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println! ("Measuring...");
|
||||||
let start = Instant::now ();
|
let start = Instant::now ();
|
||||||
for _ in 0..30
|
for _ in 0..30
|
||||||
{
|
{
|
||||||
let rc = cap.wait_for_frame (&mut buf);
|
capture.wait_for_frame (&mut buf).unwrap ();
|
||||||
bytesused = rc.unwrap ();
|
|
||||||
}
|
}
|
||||||
let stop = Instant::now ();
|
let stop = Instant::now ();
|
||||||
|
|
||||||
let capture_fps = 30_000.0f32 / (stop - start).as_millis () as f32;
|
println! ("{} ms", (stop - start).as_millis ());
|
||||||
dbg! (capture_fps);
|
|
||||||
|
|
||||||
let buf = &buf [0..bytesused];
|
|
||||||
|
|
||||||
let mut f = std::fs::File::create ("data.jpeg").unwrap ();
|
|
||||||
f.write_all (buf).unwrap ();
|
|
||||||
|
|
||||||
let start = Instant::now ();
|
|
||||||
let mut pixels = vec! [];
|
|
||||||
for _ in 0..30
|
|
||||||
{
|
|
||||||
let mut decoder = zune_jpeg::JpegDecoder::new_with_options
|
|
||||||
(
|
|
||||||
buf,
|
|
||||||
zune_core::options::DecoderOptions::new_fast ().jpeg_set_out_colorspace (zune_core::colorspace::ColorSpace::YCbCr),
|
|
||||||
);
|
|
||||||
decoder.decode_headers ().unwrap ();
|
|
||||||
pixels.resize (decoder.output_buffer_size ().unwrap (), 0);
|
|
||||||
|
|
||||||
decoder.decode_into (&mut pixels).unwrap ();
|
|
||||||
}
|
|
||||||
let stop = Instant::now ();
|
|
||||||
let decode_mpf = (stop - start).as_millis () as f32 / 30.0f32;
|
|
||||||
dbg! (decode_mpf);
|
|
||||||
|
|
||||||
let mut f = std::fs::File::create ("data.raw").unwrap ();
|
|
||||||
f.write_all (&pixels).unwrap ();
|
|
||||||
},
|
|
||||||
Some ("decode") =>
|
|
||||||
{
|
|
||||||
let jpeg = std::fs::read ("data.jpeg").unwrap ();
|
|
||||||
let start = Instant::now ();
|
|
||||||
let mut pixels = vec! [];
|
|
||||||
for _ in 0..30
|
|
||||||
{
|
|
||||||
let mut decoder = zune_jpeg::JpegDecoder::new_with_options
|
|
||||||
(
|
|
||||||
&jpeg,
|
|
||||||
zune_core::options::DecoderOptions::new_fast ().jpeg_set_out_colorspace (zune_core::colorspace::ColorSpace::YCbCr),
|
|
||||||
);
|
|
||||||
decoder.decode_headers ().unwrap ();
|
|
||||||
pixels.resize (decoder.output_buffer_size ().unwrap (), 0);
|
|
||||||
|
|
||||||
decoder.decode_into (&mut pixels).unwrap ();
|
|
||||||
}
|
|
||||||
let stop = Instant::now ();
|
|
||||||
let mpf_ycbcr = (stop - start).as_millis () as f32 / 30.0f32;
|
|
||||||
dbg! (mpf_ycbcr);
|
|
||||||
|
|
||||||
std::fs::write ("raw.data", &pixels).unwrap ();
|
|
||||||
|
|
||||||
let start = Instant::now ();
|
|
||||||
let mut pixels = vec! [];
|
|
||||||
for _ in 0..30
|
|
||||||
{
|
|
||||||
let mut decoder = zune_jpeg::JpegDecoder::new_with_options
|
|
||||||
(
|
|
||||||
&jpeg,
|
|
||||||
zune_core::options::DecoderOptions::new_fast ().jpeg_set_out_colorspace (zune_core::colorspace::ColorSpace::RGB),
|
|
||||||
);
|
|
||||||
decoder.decode_headers ().unwrap ();
|
|
||||||
pixels.resize (decoder.output_buffer_size ().unwrap (), 0);
|
|
||||||
|
|
||||||
decoder.decode_into (&mut pixels).unwrap ();
|
|
||||||
}
|
|
||||||
let stop = Instant::now ();
|
|
||||||
let mpf_rgb = (stop - start).as_millis () as f32 / 30.0f32;
|
|
||||||
dbg! (mpf_rgb);
|
|
||||||
},
|
},
|
||||||
Some (_) => eprintln! ("Unknown subcommand"),
|
Some (_) => eprintln! ("Unknown subcommand"),
|
||||||
}
|
}
|
||||||
|
@ -252,7 +181,9 @@ enum MsgToGui
|
||||||
|
|
||||||
enum MsgFromController
|
enum MsgFromController
|
||||||
{
|
{
|
||||||
Idle,
|
RepaintGui ((Vec <u8>, Instant)),
|
||||||
|
StartCapture,
|
||||||
|
StartJpegDecoder (JpegFrame),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Clone)]
|
#[derive (Clone)]
|
||||||
|
@ -262,17 +193,12 @@ struct JpegFrame
|
||||||
time: Instant,
|
time: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Driver
|
struct Controller
|
||||||
{
|
{
|
||||||
send: mpsc::SyncSender <MsgToController>,
|
gui_rgba_gen: Instant,
|
||||||
recv: mpsc::Receiver <MsgToController>,
|
|
||||||
|
|
||||||
gui_ctx: egui::Context,
|
|
||||||
send_to_gui: mpsc::SyncSender <MsgToGui>,
|
|
||||||
gui_rgba_gen: Instant,
|
|
||||||
gui_needs_frame: bool,
|
gui_needs_frame: bool,
|
||||||
|
|
||||||
capture: task::Task <capture::Capture, ()>,
|
capture: task::Task <(), ()>,
|
||||||
|
|
||||||
jpeg_decoder: task::Task <(), Instant>,
|
jpeg_decoder: task::Task <(), Instant>,
|
||||||
jpeg_needs_frame: bool,
|
jpeg_needs_frame: bool,
|
||||||
|
@ -281,27 +207,16 @@ struct Driver
|
||||||
jpeg_frame: JpegFrame,
|
jpeg_frame: JpegFrame,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Driver
|
impl Controller
|
||||||
{
|
{
|
||||||
fn new (
|
fn new (now: Instant) -> Self
|
||||||
send: mpsc::SyncSender <MsgToController>,
|
|
||||||
recv: mpsc::Receiver <MsgToController>,
|
|
||||||
gui_ctx: egui::Context,
|
|
||||||
send_to_gui: mpsc::SyncSender <MsgToGui>,
|
|
||||||
) -> Self
|
|
||||||
{
|
{
|
||||||
let now = Instant::now ();
|
Self
|
||||||
|
{
|
||||||
Self {
|
|
||||||
send,
|
|
||||||
recv,
|
|
||||||
gui_ctx,
|
|
||||||
send_to_gui,
|
|
||||||
gui_needs_frame: false,
|
gui_needs_frame: false,
|
||||||
|
|
||||||
gui_rgba_gen: now,
|
gui_rgba_gen: now,
|
||||||
|
|
||||||
capture: capture::Capture::new ().unwrap ().into (),
|
capture: ().into (),
|
||||||
|
|
||||||
jpeg_decoder: ().into (),
|
jpeg_decoder: ().into (),
|
||||||
jpeg_needs_frame: false,
|
jpeg_needs_frame: false,
|
||||||
|
@ -315,6 +230,87 @@ impl Driver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_rgba_frame (&mut self, data: Vec <u8>, gen: Instant)
|
||||||
|
{
|
||||||
|
self.jpeg_decoder.stop (()).unwrap ();
|
||||||
|
self.rgba_frame = (data, gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_capture (&mut self, jpeg: JpegFrame)
|
||||||
|
{
|
||||||
|
self.capture.stop (()).unwrap ();
|
||||||
|
self.jpeg_frame = jpeg;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_gui_needs_frame (&mut self)
|
||||||
|
{
|
||||||
|
self.gui_needs_frame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll (&mut self) -> Option <MsgFromController>
|
||||||
|
{
|
||||||
|
if self.gui_needs_frame && self.rgba_frame.1 > self.gui_rgba_gen
|
||||||
|
{
|
||||||
|
self.gui_needs_frame = false;
|
||||||
|
self.gui_rgba_gen = self.rgba_frame.1;
|
||||||
|
|
||||||
|
Some (MsgFromController::RepaintGui (self.rgba_frame.clone ()))
|
||||||
|
}
|
||||||
|
else if self.gui_needs_frame && ! self.jpeg_decoder.is_running () && self.jpeg_frame.time > self.rgba_frame.1
|
||||||
|
{
|
||||||
|
self.jpeg_decoder.start (self.jpeg_frame.time).unwrap ();
|
||||||
|
self.jpeg_needs_frame = false;
|
||||||
|
Some (MsgFromController::StartJpegDecoder (self.jpeg_frame.clone ()))
|
||||||
|
}
|
||||||
|
else if self.jpeg_needs_frame && ! self.capture.is_running ()
|
||||||
|
{
|
||||||
|
self.capture.start (()).unwrap ();
|
||||||
|
Some (MsgFromController::StartCapture)
|
||||||
|
}
|
||||||
|
else if self.gui_needs_frame && self.jpeg_frame.time == self.rgba_frame.1
|
||||||
|
{
|
||||||
|
self.jpeg_needs_frame = true;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Driver
|
||||||
|
{
|
||||||
|
send: mpsc::SyncSender <MsgToController>,
|
||||||
|
recv: mpsc::Receiver <MsgToController>,
|
||||||
|
|
||||||
|
gui_ctx: egui::Context,
|
||||||
|
send_to_gui: mpsc::SyncSender <MsgToGui>,
|
||||||
|
|
||||||
|
capture: Option <capture::Capture>,
|
||||||
|
|
||||||
|
ctl: Controller,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Driver
|
||||||
|
{
|
||||||
|
fn new (
|
||||||
|
send: mpsc::SyncSender <MsgToController>,
|
||||||
|
recv: mpsc::Receiver <MsgToController>,
|
||||||
|
gui_ctx: egui::Context,
|
||||||
|
send_to_gui: mpsc::SyncSender <MsgToGui>,
|
||||||
|
) -> Self
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
send,
|
||||||
|
recv,
|
||||||
|
gui_ctx,
|
||||||
|
send_to_gui,
|
||||||
|
capture: Some (capture::Capture::new ().unwrap ()),
|
||||||
|
ctl: Controller::new (Instant::now ()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn run (&mut self)
|
fn run (&mut self)
|
||||||
{
|
{
|
||||||
let pool = rayon::ThreadPoolBuilder::new().build().unwrap ();
|
let pool = rayon::ThreadPoolBuilder::new().build().unwrap ();
|
||||||
|
@ -325,73 +321,62 @@ impl Driver
|
||||||
{
|
{
|
||||||
MsgToController::DecodedJpegToRgba ((data, gen)) =>
|
MsgToController::DecodedJpegToRgba ((data, gen)) =>
|
||||||
{
|
{
|
||||||
self.jpeg_decoder.stop (()).unwrap ();
|
self.ctl.handle_rgba_frame (data, gen);
|
||||||
self.rgba_frame = (data, gen);
|
|
||||||
},
|
},
|
||||||
MsgToController::GotCapture ((capture, jpeg)) =>
|
MsgToController::GotCapture ((capture, jpeg)) =>
|
||||||
{
|
{
|
||||||
self.capture.stop (capture).unwrap ();
|
self.ctl.handle_capture (jpeg);
|
||||||
self.jpeg_frame = jpeg;
|
self.capture = Some (capture);
|
||||||
},
|
},
|
||||||
MsgToController::GuiNeedsRgbaFrame =>
|
MsgToController::GuiNeedsRgbaFrame =>
|
||||||
{
|
{
|
||||||
self.gui_needs_frame = true;
|
self.ctl.handle_gui_needs_frame ();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.gui_needs_frame && self.rgba_frame.1 > self.gui_rgba_gen
|
while let Some (msg) = self.ctl.poll ()
|
||||||
{
|
{
|
||||||
self.send_to_gui.send (MsgToGui::NewRgbaFrame (self.rgba_frame.clone ())).unwrap ();
|
match msg
|
||||||
self.gui_needs_frame = false;
|
|
||||||
self.gui_rgba_gen = self.rgba_frame.1;
|
|
||||||
self.gui_ctx.request_repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
if
|
|
||||||
self.gui_needs_frame &&
|
|
||||||
! self.jpeg_decoder.is_running () &&
|
|
||||||
self.jpeg_frame.time > self.rgba_frame.1
|
|
||||||
{
|
|
||||||
let send = self.send.clone ();
|
|
||||||
let jpeg_frame = self.jpeg_frame.clone ();
|
|
||||||
|
|
||||||
self.jpeg_decoder.start(jpeg_frame.time).unwrap ();
|
|
||||||
self.jpeg_needs_frame = false;
|
|
||||||
pool.spawn (move ||
|
|
||||||
{
|
{
|
||||||
let mut decoder = zune_jpeg::JpegDecoder::new_with_options (&jpeg_frame.data, zune_core::options::DecoderOptions::new_fast().jpeg_set_out_colorspace(zune_core::colorspace::ColorSpace::RGBA));
|
MsgFromController::RepaintGui (rgba_frame) =>
|
||||||
|
|
||||||
decoder.decode_headers().unwrap ();
|
|
||||||
let mut rgba = vec![0u8;decoder.output_buffer_size().unwrap ()];
|
|
||||||
decoder.decode_into(&mut rgba).unwrap ();
|
|
||||||
|
|
||||||
send.send (MsgToController::DecodedJpegToRgba ((rgba, jpeg_frame.time))).unwrap ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.gui_needs_frame && self.jpeg_frame.time == self.rgba_frame.1
|
|
||||||
{
|
|
||||||
self.jpeg_needs_frame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.jpeg_needs_frame && ! self.capture.is_running()
|
|
||||||
{
|
|
||||||
let mut capture = self.capture.start (()).unwrap ();
|
|
||||||
let send = self.send.clone ();
|
|
||||||
|
|
||||||
pool.spawn (move ||
|
|
||||||
{
|
|
||||||
let mut data = vec! [0u8; capture.size_image()];
|
|
||||||
capture.wait_for_frame(&mut data).unwrap ();
|
|
||||||
|
|
||||||
let frame = JpegFrame
|
|
||||||
{
|
{
|
||||||
data,
|
self.send_to_gui.send (MsgToGui::NewRgbaFrame (rgba_frame)).unwrap ();
|
||||||
time: Instant::now (),
|
self.gui_ctx.request_repaint();
|
||||||
};
|
},
|
||||||
|
MsgFromController::StartJpegDecoder (jpeg_frame) =>
|
||||||
send.send (MsgToController::GotCapture ((capture, frame))).unwrap ();
|
{
|
||||||
});
|
let send = self.send.clone ();
|
||||||
|
pool.spawn (move ||
|
||||||
|
{
|
||||||
|
let mut decoder = zune_jpeg::JpegDecoder::new_with_options (&jpeg_frame.data, zune_core::options::DecoderOptions::new_fast().jpeg_set_out_colorspace(zune_core::colorspace::ColorSpace::RGBA));
|
||||||
|
|
||||||
|
decoder.decode_headers().unwrap ();
|
||||||
|
let mut rgba = vec![0u8;decoder.output_buffer_size().unwrap ()];
|
||||||
|
decoder.decode_into(&mut rgba).unwrap ();
|
||||||
|
|
||||||
|
send.send (MsgToController::DecodedJpegToRgba ((rgba, jpeg_frame.time))).unwrap ();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
MsgFromController::StartCapture =>
|
||||||
|
{
|
||||||
|
let mut capture = self.capture.take ().unwrap ();
|
||||||
|
let send = self.send.clone ();
|
||||||
|
|
||||||
|
pool.spawn (move ||
|
||||||
|
{
|
||||||
|
let mut data = vec! [0u8; capture.size_image()];
|
||||||
|
capture.wait_for_frame(&mut data).unwrap ();
|
||||||
|
|
||||||
|
let frame = JpegFrame
|
||||||
|
{
|
||||||
|
data,
|
||||||
|
time: Instant::now (),
|
||||||
|
};
|
||||||
|
|
||||||
|
send.send (MsgToController::GotCapture ((capture, frame))).unwrap ();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue