⭐ now the code is pretty and it just shows the camera stream on screen
which is coolmain
parent
f87d3fc00e
commit
ef2e803c43
|
@ -22,8 +22,8 @@ impl Capture
|
|||
pub fn new () -> Result <Self, Error>
|
||||
{
|
||||
let x = Device::open ("/dev/video0")?;
|
||||
dbg! (x.formats (linuxvideo::BufType::VIDEO_CAPTURE).collect::<Vec <_>> ());
|
||||
let x = x.video_capture (PixFormat::new (1920, 1080, PixelFormat::MJPG))?;
|
||||
|
||||
let x = x.video_capture (PixFormat::new (1280, 720, PixelFormat::MJPG))?;
|
||||
x.set_frame_interval(linuxvideo::Fract::new(1, 30)).unwrap ();
|
||||
dbg! (x.format ());
|
||||
let size_image = usize::try_from (x.format ().size_image ()).unwrap ();
|
||||
|
@ -54,7 +54,7 @@ impl Capture
|
|||
{
|
||||
let bytesused = usize::try_from (view.bytesused ()).unwrap ();
|
||||
let input = &view [0..bytesused];
|
||||
&mut output [0..bytesused].copy_from_slice (&input);
|
||||
let _ = &mut output [0..bytesused].copy_from_slice (&input);
|
||||
Ok (input.len ())
|
||||
})?)
|
||||
}
|
||||
|
|
|
@ -1,298 +0,0 @@
|
|||
use std::fmt;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive (Default)]
|
||||
pub struct Controller
|
||||
{
|
||||
/// Pipeline for pulling data from the network, decoding it,
|
||||
/// and displaying it.
|
||||
|
||||
rx_pipe: RxPipeline,
|
||||
|
||||
/// Pipeline for pulling data from capture devices, encoding it,
|
||||
/// and sending it over the network.
|
||||
|
||||
tx_pipe: TxPipeline,
|
||||
}
|
||||
|
||||
#[derive (Debug)]
|
||||
pub struct ControlEvent
|
||||
{
|
||||
pub rx: Option <RxPipelineEvent>,
|
||||
pub tx: Option <TxPipelineEvent>,
|
||||
}
|
||||
|
||||
impl Controller
|
||||
{
|
||||
pub fn handle_capture_frame (&mut self, buf_raw: BufRaw)
|
||||
-> Result <(), ControlError>
|
||||
{
|
||||
self.tx_pipe.handle_capture_frame (buf_raw)
|
||||
}
|
||||
|
||||
pub fn handle_encoder_finished (&mut self)
|
||||
{
|
||||
self.tx_pipe.encoder_is_busy = false;
|
||||
}
|
||||
|
||||
pub fn handle_encoded_packet (&mut self, buf_enc: Option <BufEncoded>) -> Result <(), ControlError>
|
||||
{
|
||||
self.tx_pipe.handle_encoded_packet (buf_enc)
|
||||
}
|
||||
|
||||
pub fn handle_transmitted (&mut self, x: bool)
|
||||
{
|
||||
self.tx_pipe.handle_transmitted (x)
|
||||
}
|
||||
|
||||
pub fn poll (&mut self) -> ControlEvent
|
||||
{
|
||||
let rx = self.rx_pipe.poll ();
|
||||
let tx = self.tx_pipe.poll ();
|
||||
|
||||
ControlEvent {
|
||||
rx,
|
||||
tx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive (Default)]
|
||||
struct RxPipeline
|
||||
{
|
||||
buf_enc: Option <BufEncoded>,
|
||||
buf_raw: Option <BufRaw>,
|
||||
}
|
||||
|
||||
#[derive (Debug)]
|
||||
pub enum RxPipelineEvent
|
||||
{
|
||||
DisplayRaw (BufRaw),
|
||||
Decode (BufEncoded),
|
||||
Receive,
|
||||
}
|
||||
|
||||
#[derive (Debug, Error)]
|
||||
enum RxPipeError
|
||||
{
|
||||
#[error ("already have encoded data")]
|
||||
AlreadyHaveEncodedData,
|
||||
#[error ("already have raw data")]
|
||||
AlreadyHaveRawData,
|
||||
}
|
||||
|
||||
impl RxPipeline
|
||||
{
|
||||
|
||||
fn handle_encoder_polled (&mut self, has_data: bool)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
fn handle_raw_frame (&mut self, buf_raw: BufRaw) -> Result <(), RxPipeError>
|
||||
{
|
||||
if self.has_raw_data ()
|
||||
{
|
||||
return Err (RxPipeError::AlreadyHaveRawData);
|
||||
}
|
||||
|
||||
self.buf_raw = Some (buf_raw);
|
||||
|
||||
Ok (())
|
||||
}
|
||||
|
||||
fn poll (&mut self) -> Option <RxPipelineEvent>
|
||||
{
|
||||
if let Some (buf_raw) = self.buf_raw.take ()
|
||||
{
|
||||
return Some (RxPipelineEvent::DisplayRaw (buf_raw));
|
||||
}
|
||||
|
||||
if ! self.has_raw_data ()
|
||||
{
|
||||
if let Some (buf_enc) = self.buf_enc.take ()
|
||||
{
|
||||
return Some (RxPipelineEvent::Decode (buf_enc));
|
||||
}
|
||||
}
|
||||
|
||||
if ! self.has_encoded_data ()
|
||||
{
|
||||
return Some (RxPipelineEvent::Receive);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// True if we have a buffer of received data, ready
|
||||
/// to be decoded.
|
||||
|
||||
fn has_encoded_data (&self) -> bool
|
||||
{
|
||||
self.buf_enc.is_some ()
|
||||
}
|
||||
|
||||
fn has_raw_data (&self) -> bool
|
||||
{
|
||||
self.buf_raw.is_some ()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive (Default)]
|
||||
struct TxPipeline
|
||||
{
|
||||
buf_enc: Option <BufEncoded>,
|
||||
buf_raw: Option <BufRaw>,
|
||||
capture_is_busy: bool,
|
||||
encoder_has_data: bool,
|
||||
encoder_is_busy: bool,
|
||||
network_congested: bool,
|
||||
transmitter_is_busy: bool,
|
||||
}
|
||||
|
||||
#[derive (Debug)]
|
||||
pub enum TxPipelineEvent
|
||||
{
|
||||
/// Capture a raw frame and pass it to handle_capture_frame
|
||||
Capture,
|
||||
|
||||
/// Poll the encoder. If it returns a packet, pass it to
|
||||
/// handle_encoded_packet.
|
||||
PollEncoder,
|
||||
|
||||
/// Encode this raw frame and pass the encoded frame to
|
||||
/// handle_encoded_packet
|
||||
Encode (BufRaw),
|
||||
|
||||
/// Transmit this encoded frame to the network
|
||||
Transmit (BufEncoded),
|
||||
}
|
||||
|
||||
impl TxPipeline
|
||||
{
|
||||
fn handle_capture_frame (&mut self, buf_raw: BufRaw)
|
||||
-> Result <(), ControlError>
|
||||
{
|
||||
self.capture_is_busy = false;
|
||||
|
||||
if self.has_raw_frame ()
|
||||
{
|
||||
return Err (ControlError::AlreadyHaveRawData);
|
||||
}
|
||||
|
||||
self.buf_raw = Some (buf_raw);
|
||||
|
||||
Ok (())
|
||||
}
|
||||
|
||||
fn handle_encoded_packet (&mut self, buf_enc: Option <BufEncoded>) -> Result <(), ControlError>
|
||||
{
|
||||
self.encoder_has_data = buf_enc.is_some ();
|
||||
let buf_enc = match buf_enc
|
||||
{
|
||||
None => return Ok (()),
|
||||
Some (x) => x,
|
||||
};
|
||||
|
||||
self.encoder_is_busy = false;
|
||||
|
||||
if self.has_encoded_packet ()
|
||||
{
|
||||
return Err (ControlError::AlreadyHaveEncodedData);
|
||||
}
|
||||
|
||||
self.buf_enc = Some (buf_enc);
|
||||
|
||||
Ok (())
|
||||
}
|
||||
|
||||
fn handle_transmitted (&mut self, busy: bool)
|
||||
{
|
||||
self.network_congested = busy;
|
||||
self.transmitter_is_busy = false;
|
||||
}
|
||||
|
||||
fn has_encoded_packet (&self) -> bool
|
||||
{
|
||||
self.buf_enc.is_some ()
|
||||
}
|
||||
|
||||
fn has_raw_frame (&self) -> bool
|
||||
{
|
||||
self.buf_raw.is_some ()
|
||||
}
|
||||
|
||||
fn poll (&mut self) -> Option <TxPipelineEvent>
|
||||
{
|
||||
if ! self.network_congested && ! self.transmitter_is_busy
|
||||
{
|
||||
if let Some (buf_enc) = self.buf_enc.take ()
|
||||
{
|
||||
self.transmitter_is_busy = true;
|
||||
return Some (TxPipelineEvent::Transmit (buf_enc));
|
||||
}
|
||||
}
|
||||
|
||||
if ! self.has_encoded_packet () && ! self.encoder_is_busy
|
||||
{
|
||||
if self.encoder_has_data
|
||||
{
|
||||
return Some (TxPipelineEvent::PollEncoder);
|
||||
}
|
||||
|
||||
if let Some (buf_raw) = self.buf_raw.take ()
|
||||
{
|
||||
self.encoder_has_data = true;
|
||||
self.encoder_is_busy = true;
|
||||
return Some (TxPipelineEvent::Encode (buf_raw));
|
||||
}
|
||||
}
|
||||
|
||||
if ! self.has_raw_frame ()
|
||||
{
|
||||
if ! self.capture_is_busy
|
||||
{
|
||||
self.capture_is_busy = true;
|
||||
return Some (TxPipelineEvent::Capture);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BufEncoded
|
||||
{
|
||||
pub buf: Vec <u8>
|
||||
}
|
||||
|
||||
impl fmt::Debug for BufEncoded {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f
|
||||
.debug_struct("BufEncoded")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BufRaw
|
||||
{
|
||||
pub buf: Vec <u8>
|
||||
}
|
||||
|
||||
impl fmt::Debug for BufRaw {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f
|
||||
.debug_struct("BufRaw")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive (Debug, Error)]
|
||||
pub enum ControlError
|
||||
{
|
||||
#[error ("already have encoded data")]
|
||||
AlreadyHaveEncodedData,
|
||||
#[error ("already have raw data")]
|
||||
AlreadyHaveRawData,
|
||||
}
|
670
src/main.rs
670
src/main.rs
|
@ -12,10 +12,8 @@ use std::{
|
|||
use eframe::egui;
|
||||
use rayon::ThreadPool;
|
||||
|
||||
use crate::controller::*;
|
||||
|
||||
mod capture;
|
||||
mod controller;
|
||||
mod task;
|
||||
|
||||
fn main() -> Result <(), Error>
|
||||
{
|
||||
|
@ -23,12 +21,7 @@ fn main() -> Result <(), Error>
|
|||
let _exe_name = args.next ();
|
||||
match args.next ().as_deref ()
|
||||
{
|
||||
None =>
|
||||
{
|
||||
let pool = rayon::ThreadPoolBuilder::new ().build ().unwrap ();
|
||||
let mut driver = Driver::new (&pool)?;
|
||||
driver.main ()?;
|
||||
},
|
||||
None => main_egui (),
|
||||
Some ("capture") =>
|
||||
{
|
||||
use std::io::Write;
|
||||
|
@ -38,7 +31,7 @@ fn main() -> Result <(), Error>
|
|||
|
||||
for _ in 0..30
|
||||
{
|
||||
cap.wait_for_frame (&mut buf);
|
||||
cap.wait_for_frame (&mut buf).unwrap ();
|
||||
}
|
||||
|
||||
let start = Instant::now ();
|
||||
|
@ -67,7 +60,6 @@ fn main() -> Result <(), Error>
|
|||
zune_core::options::DecoderOptions::new_fast ().jpeg_set_out_colorspace (zune_core::colorspace::ColorSpace::YCbCr),
|
||||
);
|
||||
decoder.decode_headers ().unwrap ();
|
||||
dbg! (decoder.get_output_colorspace ());
|
||||
pixels.resize (decoder.output_buffer_size ().unwrap (), 0);
|
||||
|
||||
decoder.decode_into (&mut pixels).unwrap ();
|
||||
|
@ -100,7 +92,7 @@ fn main() -> Result <(), Error>
|
|||
let mpf_ycbcr = (stop - start).as_millis () as f32 / 30.0f32;
|
||||
dbg! (mpf_ycbcr);
|
||||
|
||||
std::fs::write ("raw.data", &pixels);
|
||||
std::fs::write ("raw.data", &pixels).unwrap ();
|
||||
|
||||
let start = Instant::now ();
|
||||
let mut pixels = vec! [];
|
||||
|
@ -120,27 +112,50 @@ fn main() -> Result <(), Error>
|
|||
let mpf_rgb = (stop - start).as_millis () as f32 / 30.0f32;
|
||||
dbg! (mpf_rgb);
|
||||
},
|
||||
Some ("egui") => main_egui (),
|
||||
Some (_) => eprintln! ("Unknown subcommand"),
|
||||
}
|
||||
|
||||
Ok (())
|
||||
}
|
||||
|
||||
struct MyEguiApp {
|
||||
|
||||
|
||||
struct App {
|
||||
img: egui::ColorImage,
|
||||
texture: Option <egui::TextureHandle>,
|
||||
capture: capture::Capture,
|
||||
gui_frames: usize,
|
||||
camera_frames: usize,
|
||||
gui_frames: u64,
|
||||
camera_frames: u64,
|
||||
gui_fps: u64,
|
||||
camera_fps: u64,
|
||||
latency: Duration,
|
||||
|
||||
send_to_ctl: mpsc::SyncSender <MsgToController>,
|
||||
recv_at_gui: mpsc::Receiver <MsgToGui>,
|
||||
driver_thread: thread::JoinHandle <()>,
|
||||
|
||||
requesting_frames: bool,
|
||||
|
||||
next_stat_refresh: Instant,
|
||||
}
|
||||
|
||||
impl MyEguiApp {
|
||||
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||
// Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
|
||||
// Restore app state using cc.storage (requires the "persistence" feature).
|
||||
// Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
|
||||
// for e.g. egui::PaintCallback.
|
||||
impl App {
|
||||
fn new (
|
||||
cc: &eframe::CreationContext<'_>,
|
||||
) -> Self
|
||||
{
|
||||
let (send_to_ctl, recv_at_ctl) = mpsc::sync_channel (8);
|
||||
let (send_to_gui, recv_at_gui) = mpsc::sync_channel (8);
|
||||
|
||||
let gui_ctx = cc.egui_ctx.clone ();
|
||||
|
||||
let mut driver = Driver::new
|
||||
(
|
||||
send_to_ctl.clone (),
|
||||
recv_at_ctl,
|
||||
gui_ctx,
|
||||
send_to_gui,
|
||||
);
|
||||
let driver_thread = thread::spawn (move || driver.run ());
|
||||
|
||||
let img = egui::ColorImage::new ([1280,720], egui::Color32::TEMPORARY_COLOR);
|
||||
|
||||
|
@ -148,14 +163,21 @@ impl MyEguiApp {
|
|||
{
|
||||
img,
|
||||
texture: None,
|
||||
capture: capture::Capture::new ().unwrap (),
|
||||
gui_frames: 0,
|
||||
camera_frames: 0,
|
||||
gui_fps: 0,
|
||||
camera_fps: 0,
|
||||
latency: Duration::from_millis(0),
|
||||
send_to_ctl,
|
||||
recv_at_gui,
|
||||
driver_thread,
|
||||
requesting_frames: true,
|
||||
next_stat_refresh: Instant::now (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for MyEguiApp {
|
||||
impl eframe::App for App {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
let texture = self.texture.get_or_insert_with(||
|
||||
|
@ -163,20 +185,37 @@ impl eframe::App for MyEguiApp {
|
|||
ui.ctx ().load_texture ("my_image", self.img.clone (), Default::default())
|
||||
});
|
||||
|
||||
match self.capture.will_block ()
|
||||
if self.requesting_frames
|
||||
{
|
||||
Ok (false) =>
|
||||
self.send_to_ctl.send (MsgToController::GuiNeedsRgbaFrame).unwrap ();
|
||||
}
|
||||
|
||||
while let Ok (msg) = self.recv_at_gui.try_recv()
|
||||
{
|
||||
match msg
|
||||
{
|
||||
self.capture.wait_for_frame(self.img.as_raw_mut()).unwrap ();
|
||||
texture.set (self.img.clone (), Default::default ());
|
||||
self.camera_frames += 1;
|
||||
},
|
||||
_ => (),
|
||||
MsgToGui::NewRgbaFrame ((data, gen)) =>
|
||||
{
|
||||
self.latency = Instant::now () - gen;
|
||||
self.camera_frames += 1;
|
||||
texture.set (egui::ColorImage::from_rgba_premultiplied([1280,720], &data), Default::default ());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let texture: &egui::TextureHandle = texture;
|
||||
|
||||
ui.heading (format! ("{0}, {1}", self.camera_frames, self.gui_frames));
|
||||
let now = Instant::now ();
|
||||
if now >= self.next_stat_refresh
|
||||
{
|
||||
self.gui_fps = self.gui_frames;
|
||||
self.camera_fps = self.camera_frames;
|
||||
self.gui_frames = 0;
|
||||
self.camera_frames = 0;
|
||||
self.next_stat_refresh += Duration::from_secs(1);
|
||||
}
|
||||
|
||||
ui.heading (format! ("{0}, {1}, {2}", self.camera_fps, self.gui_fps, self.latency.as_millis ()));
|
||||
let available = ui.available_size();
|
||||
let tex_size = egui::Vec2::new (texture.size()[0] as f32, texture.size()[1] as f32);
|
||||
let scaled_width = available.y * tex_size.x / tex_size.y;
|
||||
|
@ -193,417 +232,176 @@ impl eframe::App for MyEguiApp {
|
|||
ui.image (texture, size);
|
||||
|
||||
self.gui_frames += 1;
|
||||
|
||||
ui.checkbox(&mut self.requesting_frames, "Run");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
enum MsgToController
|
||||
{
|
||||
GuiNeedsRgbaFrame,
|
||||
GotCapture ((capture::Capture, JpegFrame)),
|
||||
DecodedJpegToRgba ((Vec <u8>, Instant)),
|
||||
}
|
||||
|
||||
enum MsgToGui
|
||||
{
|
||||
NewRgbaFrame ((Vec <u8>, Instant)),
|
||||
}
|
||||
|
||||
enum MsgFromController
|
||||
{
|
||||
Idle,
|
||||
}
|
||||
|
||||
#[derive (Clone)]
|
||||
struct JpegFrame
|
||||
{
|
||||
data: Vec <u8>,
|
||||
time: Instant,
|
||||
}
|
||||
|
||||
struct Driver
|
||||
{
|
||||
send: mpsc::SyncSender <MsgToController>,
|
||||
recv: mpsc::Receiver <MsgToController>,
|
||||
|
||||
gui_ctx: egui::Context,
|
||||
send_to_gui: mpsc::SyncSender <MsgToGui>,
|
||||
gui_rgba_gen: Instant,
|
||||
gui_needs_frame: bool,
|
||||
|
||||
capture: task::Task <capture::Capture, ()>,
|
||||
|
||||
jpeg_decoder: task::Task <(), Instant>,
|
||||
jpeg_needs_frame: bool,
|
||||
|
||||
rgba_frame: (Vec <u8>, Instant),
|
||||
jpeg_frame: JpegFrame,
|
||||
}
|
||||
|
||||
impl Driver
|
||||
{
|
||||
fn new (
|
||||
send: mpsc::SyncSender <MsgToController>,
|
||||
recv: mpsc::Receiver <MsgToController>,
|
||||
gui_ctx: egui::Context,
|
||||
send_to_gui: mpsc::SyncSender <MsgToGui>,
|
||||
) -> Self
|
||||
{
|
||||
let now = Instant::now ();
|
||||
|
||||
ctx.request_repaint();
|
||||
Self {
|
||||
send,
|
||||
recv,
|
||||
gui_ctx,
|
||||
send_to_gui,
|
||||
gui_needs_frame: false,
|
||||
|
||||
gui_rgba_gen: now,
|
||||
|
||||
capture: capture::Capture::new ().unwrap ().into (),
|
||||
|
||||
jpeg_decoder: ().into (),
|
||||
jpeg_needs_frame: false,
|
||||
|
||||
rgba_frame: (Default::default (), now),
|
||||
jpeg_frame: JpegFrame
|
||||
{
|
||||
data: Default::default (),
|
||||
time: now,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn run (&mut self)
|
||||
{
|
||||
let pool = rayon::ThreadPoolBuilder::new().build().unwrap ();
|
||||
|
||||
loop
|
||||
{
|
||||
match self.recv.recv().unwrap ()
|
||||
{
|
||||
MsgToController::DecodedJpegToRgba ((data, gen)) =>
|
||||
{
|
||||
self.jpeg_decoder.stop (()).unwrap ();
|
||||
self.rgba_frame = (data, gen);
|
||||
},
|
||||
MsgToController::GotCapture ((capture, jpeg)) =>
|
||||
{
|
||||
self.capture.stop (capture).unwrap ();
|
||||
self.jpeg_frame = jpeg;
|
||||
},
|
||||
MsgToController::GuiNeedsRgbaFrame =>
|
||||
{
|
||||
self.gui_needs_frame = true;
|
||||
},
|
||||
}
|
||||
|
||||
if self.gui_needs_frame && self.rgba_frame.1 > self.gui_rgba_gen
|
||||
{
|
||||
self.send_to_gui.send (MsgToGui::NewRgbaFrame (self.rgba_frame.clone ())).unwrap ();
|
||||
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));
|
||||
|
||||
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,
|
||||
time: Instant::now (),
|
||||
};
|
||||
|
||||
send.send (MsgToController::GotCapture ((capture, frame))).unwrap ();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main_egui ()
|
||||
{
|
||||
let native_options = eframe::NativeOptions::default();
|
||||
eframe::run_native("My egui App", native_options, Box::new(|cc| Box::new(MyEguiApp::new(cc)))).unwrap ();
|
||||
}
|
||||
|
||||
struct ThreadSystem <'a>
|
||||
{
|
||||
pool: &'a ThreadPool,
|
||||
send: mpsc::SyncSender <TaskOutput>,
|
||||
}
|
||||
|
||||
impl <'a> ThreadSystem <'a>
|
||||
{
|
||||
fn dispatch <F: FnOnce () -> TaskOutput + Send + 'static> (&self, work: F)
|
||||
{
|
||||
let send = self.send.clone ();
|
||||
self.pool.spawn (move ||
|
||||
{
|
||||
let output = work ();
|
||||
send.send (output).ok ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Driver <'a>
|
||||
{
|
||||
thread_sys: ThreadSystem <'a>,
|
||||
recv: mpsc::Receiver <TaskOutput>,
|
||||
|
||||
ctl: Controller,
|
||||
|
||||
capture: Task <capture::Capture, ()>,
|
||||
encoder: Task <Encoder, EncoderTaskMetadata>,
|
||||
transmitter: Task <Transmitter, ()>,
|
||||
}
|
||||
|
||||
impl <'a> Driver <'_>
|
||||
{
|
||||
fn new (pool: &'a ThreadPool) -> Result <Driver <'a>, Error>
|
||||
{
|
||||
let (send, recv) = mpsc::sync_channel (8);
|
||||
|
||||
let thread_sys = ThreadSystem
|
||||
{
|
||||
pool,
|
||||
send,
|
||||
};
|
||||
|
||||
Ok (Driver
|
||||
{
|
||||
thread_sys,
|
||||
recv,
|
||||
|
||||
ctl: Default::default (),
|
||||
|
||||
capture: capture::Capture::new ()?.into (),
|
||||
encoder: Encoder::default ().into (),
|
||||
transmitter: Transmitter::default ().into (),
|
||||
})
|
||||
}
|
||||
|
||||
fn main (&mut self) -> Result <(), TaskError>
|
||||
{
|
||||
for _ in 0..50
|
||||
{
|
||||
let ev = self.ctl.poll ();
|
||||
|
||||
if let Some (event) = ev.tx
|
||||
{
|
||||
dbg! (&event);
|
||||
self.handle_tx_event (event)?;
|
||||
|
||||
match self.recv.try_recv ()
|
||||
{
|
||||
Ok (output) => self.handle_task_output (output)?,
|
||||
Err (_) => (),
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
match self.recv.recv ()
|
||||
{
|
||||
Ok (output) => self.handle_task_output (output)?,
|
||||
Err (_) => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok (())
|
||||
}
|
||||
|
||||
fn handle_task_output (&mut self, output: TaskOutput) -> Result <(), TaskError>
|
||||
{
|
||||
dbg! (&output);
|
||||
match output
|
||||
{
|
||||
TaskOutput::TxCapture (work) => work.finish (self)?,
|
||||
TaskOutput::TxEncode (work) => work.finish (self)?,
|
||||
TaskOutput::TxTransmit (work) => work.finish (self)?,
|
||||
}
|
||||
|
||||
Ok (())
|
||||
}
|
||||
|
||||
fn handle_tx_event (&mut self, event: TxPipelineEvent) -> Result <(), TaskError>
|
||||
{
|
||||
match event
|
||||
{
|
||||
TxPipelineEvent::Capture => CaptureWork::dispatch (self, ())?,
|
||||
TxPipelineEvent::Encode (buf_raw) => EncodeWork::dispatch (self, buf_raw)?,
|
||||
TxPipelineEvent::Transmit (buf_enc) => TransmitWork::dispatch (self, buf_enc)?,
|
||||
|
||||
TxPipelineEvent::PollEncoder =>
|
||||
{
|
||||
let encoder = self.encoder.try_inner_mut ()?;
|
||||
|
||||
let mut buf = vec! [];
|
||||
|
||||
let may_have_data = encoder.poll_encoded (&mut buf);
|
||||
if may_have_data
|
||||
{
|
||||
self.ctl.handle_encoded_packet (Some (BufEncoded {buf})).unwrap ();
|
||||
}
|
||||
else
|
||||
{
|
||||
self.ctl.handle_encoded_packet (None).unwrap ();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok (())
|
||||
}
|
||||
}
|
||||
|
||||
/// This is probably just a fancy Cell or something.
|
||||
|
||||
enum Task <S, R>
|
||||
{
|
||||
Stopped (S),
|
||||
Running (R),
|
||||
}
|
||||
|
||||
#[derive (Debug, thiserror::Error)]
|
||||
enum TaskError
|
||||
{
|
||||
#[error ("tried to start already-running task")]
|
||||
AlreadyRunning,
|
||||
#[error ("tried to stop already-stopped task")]
|
||||
AlreadyStopped,
|
||||
#[error ("cannot access a task's inner object while it's running")]
|
||||
CantAccessWhileRunning,
|
||||
}
|
||||
|
||||
impl <S, R> From <S> for Task <S, R>
|
||||
{
|
||||
fn from (x: S) -> Self
|
||||
{
|
||||
Self::Stopped (x)
|
||||
}
|
||||
}
|
||||
|
||||
impl <S, R> Task <S, R>
|
||||
{
|
||||
fn try_inner (&self) -> Result <&S, TaskError>
|
||||
{
|
||||
match self
|
||||
{
|
||||
Self::Running (_) => Err (TaskError::CantAccessWhileRunning),
|
||||
Self::Stopped (x) => Ok (x),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_inner_mut (&mut self) -> Result <&mut S, TaskError>
|
||||
{
|
||||
match self
|
||||
{
|
||||
Self::Running (_) => Err (TaskError::CantAccessWhileRunning),
|
||||
Self::Stopped (x) => Ok (x),
|
||||
}
|
||||
}
|
||||
|
||||
fn start (&mut self, x: R) -> Result <S, TaskError>
|
||||
{
|
||||
match std::mem::replace (self, Self::Running (x))
|
||||
{
|
||||
Self::Running (_) => Err (TaskError::AlreadyRunning),
|
||||
Self::Stopped (x) => Ok (x),
|
||||
}
|
||||
}
|
||||
|
||||
fn stop (&mut self, x: S) -> Result <R, TaskError>
|
||||
{
|
||||
match std::mem::replace (self, Self::Stopped (x))
|
||||
{
|
||||
Self::Stopped (_) => Err (TaskError::AlreadyStopped),
|
||||
Self::Running (x) => Ok (x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EncoderTaskMetadata
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
enum TaskOutput
|
||||
{
|
||||
TxCapture (CaptureWork),
|
||||
TxEncode (EncodeWork),
|
||||
TxTransmit (TransmitWork),
|
||||
}
|
||||
|
||||
impl fmt::Debug for TaskOutput {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self
|
||||
{
|
||||
Self::TxCapture (_) => f.debug_tuple ("TaskOutput::TxCapture").finish (),
|
||||
Self::TxEncode (_) => f.debug_tuple ("TaskOutput::TxEncode").finish (),
|
||||
Self::TxTransmit (_) => f.debug_tuple ("TaskOutput::TxTransmit").finish (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps non-thread-safe encoder state like from ffmpeg
|
||||
|
||||
#[derive (Default)]
|
||||
struct Encoder
|
||||
{
|
||||
/// AVCodecContext
|
||||
ctx: (),
|
||||
|
||||
/// Encoded AVPacket
|
||||
pkt_enc: Option <()>,
|
||||
|
||||
/// Bogus debugging thing
|
||||
internal_buffer: usize,
|
||||
}
|
||||
|
||||
impl Encoder
|
||||
{
|
||||
/// If any encoded bytes are ready, and the caller's buffer
|
||||
/// is big enough, copy encoded bytes into the caller's buffer,
|
||||
/// consuming them from internal buffers.
|
||||
///
|
||||
/// Won't block
|
||||
|
||||
fn poll_encoded (&mut self, _buf_enc: &mut [u8]) -> bool
|
||||
{
|
||||
if let Some (_pkt) = self.pkt_enc.take ()
|
||||
{
|
||||
// Do copy
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.internal_buffer >= 2
|
||||
{
|
||||
self.internal_buffer -= 2;
|
||||
// Do copy
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Insert raw data. If the internal buffers are full, the new
|
||||
/// data is rejected.
|
||||
/// Call poll_encoded before calling this, to drain internal
|
||||
/// buffers if needed.
|
||||
///
|
||||
/// Blocks if the internals perform encoding
|
||||
|
||||
fn try_handle_insert_data (&mut self, _buf_raw: &[u8]) -> Result <(), ControlError>
|
||||
{
|
||||
if self.pkt_enc.is_some ()
|
||||
{
|
||||
return Err (ControlError::AlreadyHaveRawData);
|
||||
}
|
||||
|
||||
if self.internal_buffer >= 3
|
||||
{
|
||||
return Err (ControlError::AlreadyHaveRawData);
|
||||
}
|
||||
|
||||
self.internal_buffer += 3;
|
||||
|
||||
Ok (())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a network transmitter like a TCP send stream or a QUIC
|
||||
/// outgoing uni stream
|
||||
|
||||
#[derive (Default)]
|
||||
struct Transmitter
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
impl Transmitter
|
||||
{
|
||||
/// Tries to transmit data over the network.
|
||||
/// If Transmitter sees congestion, it may reject the transmission
|
||||
/// until a later time.
|
||||
|
||||
fn try_send (&mut self, _now: Instant, _buf: &[u8]) -> bool
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct CaptureWork
|
||||
{
|
||||
cap: capture::Capture,
|
||||
buf_raw: BufRaw,
|
||||
}
|
||||
|
||||
impl CaptureWork
|
||||
{
|
||||
fn dispatch (driver: &mut Driver, _input: ()) -> Result <(), TaskError>
|
||||
{
|
||||
let metadata = ();
|
||||
let cap = driver.capture.start (metadata)?;
|
||||
driver.thread_sys.dispatch (move ||
|
||||
{
|
||||
let dur_capture = Duration::from_millis (1000);
|
||||
thread::sleep (dur_capture);
|
||||
let buf_raw = BufRaw
|
||||
{
|
||||
buf: vec![],
|
||||
};
|
||||
TaskOutput::TxCapture (Self {cap, buf_raw})
|
||||
});
|
||||
Ok (())
|
||||
}
|
||||
|
||||
fn finish (self, driver: &mut Driver) -> Result <(), TaskError>
|
||||
{
|
||||
driver.capture.stop (self.cap)?;
|
||||
driver.ctl.handle_capture_frame (self.buf_raw).unwrap ();
|
||||
Ok (())
|
||||
}
|
||||
}
|
||||
|
||||
struct EncodeWork
|
||||
{
|
||||
encoder: Encoder,
|
||||
}
|
||||
|
||||
impl EncodeWork
|
||||
{
|
||||
fn dispatch (driver: &mut Driver, buf_raw: BufRaw) -> Result <(), TaskError>
|
||||
{
|
||||
let mut encoder = driver.encoder.start (EncoderTaskMetadata {})?;
|
||||
driver.thread_sys.dispatch (move ||
|
||||
{
|
||||
let dur_encode = Duration::from_millis (20);
|
||||
thread::sleep (dur_encode);
|
||||
encoder.try_handle_insert_data (&buf_raw.buf).unwrap ();
|
||||
TaskOutput::TxEncode (Self {encoder})
|
||||
});
|
||||
Ok (())
|
||||
}
|
||||
|
||||
fn finish (self, driver: &mut Driver) -> Result <(), TaskError>
|
||||
{
|
||||
let _metadata = driver.encoder.stop (self.encoder)?;
|
||||
driver.ctl.handle_encoder_finished ();
|
||||
Ok (())
|
||||
}
|
||||
}
|
||||
|
||||
struct TransmitWork
|
||||
{
|
||||
tx: Transmitter,
|
||||
busy: bool,
|
||||
}
|
||||
|
||||
impl TransmitWork
|
||||
{
|
||||
fn dispatch (driver: &mut Driver, buf_enc: BufEncoded) -> Result <(), TaskError>
|
||||
{
|
||||
let metadata = ();
|
||||
let mut tx = driver.transmitter.start (metadata)?;
|
||||
driver.thread_sys.dispatch (move ||
|
||||
{
|
||||
let dur_transmit = Duration::from_millis (200);
|
||||
thread::sleep (dur_transmit);
|
||||
let busy = tx.try_send (Instant::now (), &buf_enc.buf);
|
||||
TaskOutput::TxTransmit (Self
|
||||
{
|
||||
tx,
|
||||
busy,
|
||||
})
|
||||
});
|
||||
|
||||
Ok (())
|
||||
}
|
||||
|
||||
fn finish (self, driver: &mut Driver) -> Result <(), TaskError>
|
||||
{
|
||||
driver.transmitter.stop (self.tx)?;
|
||||
driver.ctl.handle_transmitted (self.busy);
|
||||
Ok (())
|
||||
}
|
||||
eframe::run_native("Five Five Five", native_options, Box::new(|cc| Box::new(App::new(cc)))).unwrap ();
|
||||
}
|
||||
|
||||
#[derive (Debug, thiserror::Error)]
|
||||
|
@ -612,5 +410,5 @@ enum Error
|
|||
#[error ("capture")]
|
||||
Capture (#[from] capture::Error),
|
||||
#[error ("task")]
|
||||
Task (#[from] TaskError),
|
||||
Task (#[from] task::Error),
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/// This is probably just a fancy Cell or something.
|
||||
|
||||
pub enum Task <S, R>
|
||||
{
|
||||
Stopped (S),
|
||||
Running (R),
|
||||
}
|
||||
|
||||
#[derive (Debug, thiserror::Error)]
|
||||
pub enum Error
|
||||
{
|
||||
#[error ("tried to start already-running task")]
|
||||
AlreadyRunning,
|
||||
#[error ("tried to stop already-stopped task")]
|
||||
AlreadyStopped,
|
||||
#[error ("cannot access a task's inner object while it's running")]
|
||||
CantAccessWhileRunning,
|
||||
}
|
||||
|
||||
impl <S, R> From <S> for Task <S, R>
|
||||
{
|
||||
fn from (x: S) -> Self
|
||||
{
|
||||
Self::Stopped (x)
|
||||
}
|
||||
}
|
||||
|
||||
impl <S, R> Task <S, R>
|
||||
{
|
||||
pub fn is_running (&self) -> bool
|
||||
{
|
||||
match self
|
||||
{
|
||||
Self::Running (_) => true,
|
||||
Self::Stopped (_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_inner (&self) -> Result <&S, Error>
|
||||
{
|
||||
match self
|
||||
{
|
||||
Self::Running (_) => Err (Error::CantAccessWhileRunning),
|
||||
Self::Stopped (x) => Ok (x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_inner_mut (&mut self) -> Result <&mut S, Error>
|
||||
{
|
||||
match self
|
||||
{
|
||||
Self::Running (_) => Err (Error::CantAccessWhileRunning),
|
||||
Self::Stopped (x) => Ok (x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start (&mut self, x: R) -> Result <S, Error>
|
||||
{
|
||||
match std::mem::replace (self, Self::Running (x))
|
||||
{
|
||||
Self::Running (_) => Err (Error::AlreadyRunning),
|
||||
Self::Stopped (x) => Ok (x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop (&mut self, x: S) -> Result <R, Error>
|
||||
{
|
||||
match std::mem::replace (self, Self::Stopped (x))
|
||||
{
|
||||
Self::Stopped (_) => Err (Error::AlreadyStopped),
|
||||
Self::Running (x) => Ok (x),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue