From ef2e803c43a8634782cb30ba28a09bccb2882aab Mon Sep 17 00:00:00 2001 From: _ <_@_> Date: Sun, 10 Sep 2023 23:10:54 -0500 Subject: [PATCH] :star: now the code is pretty and it just shows the camera stream on screen which is cool --- src/capture.rs | 6 +- src/controller.rs | 298 --------------------- src/main.rs | 670 ++++++++++++++++------------------------------ src/task.rs | 74 +++++ 4 files changed, 311 insertions(+), 737 deletions(-) delete mode 100644 src/controller.rs create mode 100644 src/task.rs diff --git a/src/capture.rs b/src/capture.rs index 8631558..4537a2c 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -22,8 +22,8 @@ impl Capture pub fn new () -> Result { let x = Device::open ("/dev/video0")?; - dbg! (x.formats (linuxvideo::BufType::VIDEO_CAPTURE).collect::> ()); - 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 ()) })?) } diff --git a/src/controller.rs b/src/controller.rs deleted file mode 100644 index a949d38..0000000 --- a/src/controller.rs +++ /dev/null @@ -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 , - pub tx: Option , -} - -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 ) -> 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 , - buf_raw: Option , -} - -#[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 - { - 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 , - buf_raw: Option , - 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 ) -> 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 - { - 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 -} - -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 -} - -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, -} diff --git a/src/main.rs b/src/main.rs index e5c15fb..1984e93 100644 --- a/src/main.rs +++ b/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 , - 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 , + recv_at_gui: mpsc::Receiver , + 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 , Instant)), +} + +enum MsgToGui +{ + NewRgbaFrame ((Vec , Instant)), +} + +enum MsgFromController +{ + Idle, +} + +#[derive (Clone)] +struct JpegFrame +{ + data: Vec , + time: Instant, +} + +struct Driver +{ + send: mpsc::SyncSender , + recv: mpsc::Receiver , + + gui_ctx: egui::Context, + send_to_gui: mpsc::SyncSender , + gui_rgba_gen: Instant, + gui_needs_frame: bool, + + capture: task::Task , + + jpeg_decoder: task::Task <(), Instant>, + jpeg_needs_frame: bool, + + rgba_frame: (Vec , Instant), + jpeg_frame: JpegFrame, +} + +impl Driver +{ + fn new ( + send: mpsc::SyncSender , + recv: mpsc::Receiver , + gui_ctx: egui::Context, + send_to_gui: mpsc::SyncSender , + ) -> 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 , -} - -impl <'a> ThreadSystem <'a> -{ - fn dispatch 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 , - ctl: Controller, - - capture: Task , - encoder: Task , - transmitter: Task , -} - -impl <'a> Driver <'_> -{ - fn new (pool: &'a ThreadPool) -> Result , 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 -{ - 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 From for Task -{ - fn from (x: S) -> Self - { - Self::Stopped (x) - } -} - -impl Task -{ - 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 - { - 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 - { - 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), } diff --git a/src/task.rs b/src/task.rs new file mode 100644 index 0000000..dffde0a --- /dev/null +++ b/src/task.rs @@ -0,0 +1,74 @@ +/// This is probably just a fancy Cell or something. + +pub enum Task +{ + 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 From for Task +{ + fn from (x: S) -> Self + { + Self::Stopped (x) + } +} + +impl Task +{ + 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 + { + 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 + { + match std::mem::replace (self, Self::Stopped (x)) + { + Self::Stopped (_) => Err (Error::AlreadyStopped), + Self::Running (x) => Ok (x), + } + } +}