capturing MJPG

main
_ 2023-09-09 13:31:37 -05:00
parent 28daa28d90
commit 3da0836c8a
6 changed files with 186 additions and 43 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/deps
/target /target

52
Cargo.lock generated
View File

@ -8,6 +8,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -44,7 +50,7 @@ dependencies = [
"autocfg", "autocfg",
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
"memoffset", "memoffset 0.9.0",
"scopeguard", "scopeguard",
] ]
@ -67,6 +73,7 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
name = "five_five_five" name = "five_five_five"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"linuxvideo",
"rayon", "rayon",
"thiserror", "thiserror",
] ]
@ -83,6 +90,30 @@ version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "linuxvideo"
version = "0.3.1"
dependencies = [
"bitflags",
"log",
"nix",
]
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.9.0" version = "0.9.0"
@ -92,6 +123,19 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "nix"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags",
"cfg-if",
"libc",
"memoffset 0.7.1",
"pin-utils",
]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.16.0" version = "1.16.0"
@ -102,6 +146,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.66" version = "1.0.66"

View File

@ -6,5 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
linuxvideo = { path = "deps/LinuxVideo" }
rayon = "1.7.0" rayon = "1.7.0"
thiserror = "1.0.48" thiserror = "1.0.48"

69
src/capture.rs Normal file
View File

@ -0,0 +1,69 @@
/// Wraps non-thread-safe capture device like a v4l webcam
use linuxvideo::
{
format::
{
PixFormat,
PixelFormat,
},
Device,
stream::ReadStream,
};
pub struct Capture
{
size_image: usize,
stream: ReadStream,
}
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 (u32::MAX, u32::MAX, PixelFormat::MJPG))?;
dbg! (x.format ());
let size_image = usize::try_from (x.format ().size_image ()).unwrap ();
let stream = x.into_stream ()?;
Ok (Self
{
size_image,
stream,
})
}
pub fn size_image (&self) -> usize
{
self.size_image
}
/// Blocks until the capture device gets us a frame
pub fn wait_for_frame (&mut self, output: &mut [u8]) -> Result <usize, Error>
{
if output.len () < self.size_image ()
{
return Err (Error::OutputBufferTooSmall);
}
Ok (self.stream.dequeue (|view|
{
let bytesused = usize::try_from (view.bytesused ()).unwrap ();
let input = &view [0..bytesused];
&mut output [0..bytesused].copy_from_slice (&input);
Ok (input.len ())
})?)
}
}
#[derive (Debug, thiserror::Error)]
pub enum Error
{
#[error ("I/O")]
Io (#[from] std::io::Error),
#[error ("output buffer too small")]
OutputBufferTooSmall,
}

View File

@ -41,9 +41,9 @@ impl Controller
self.tx_pipe.handle_encoded_packet (buf_enc) self.tx_pipe.handle_encoded_packet (buf_enc)
} }
pub fn handle_network_busy (&mut self, x: bool) pub fn handle_transmitted (&mut self, x: bool)
{ {
self.tx_pipe.handle_network_busy (x) self.tx_pipe.handle_transmitted (x)
} }
pub fn poll (&mut self) -> ControlEvent pub fn poll (&mut self) -> ControlEvent
@ -147,7 +147,8 @@ struct TxPipeline
capture_is_busy: bool, capture_is_busy: bool,
encoder_has_data: bool, encoder_has_data: bool,
encoder_is_busy: bool, encoder_is_busy: bool,
network_busy: bool, network_congested: bool,
transmitter_is_busy: bool,
} }
#[derive (Debug)] #[derive (Debug)]
@ -206,9 +207,10 @@ impl TxPipeline
Ok (()) Ok (())
} }
fn handle_network_busy (&mut self, busy: bool) fn handle_transmitted (&mut self, busy: bool)
{ {
self.network_busy = busy; self.network_congested = busy;
self.transmitter_is_busy = false;
} }
fn has_encoded_packet (&self) -> bool fn has_encoded_packet (&self) -> bool
@ -223,10 +225,11 @@ impl TxPipeline
fn poll (&mut self) -> Option <TxPipelineEvent> fn poll (&mut self) -> Option <TxPipelineEvent>
{ {
if ! self.network_busy if ! self.network_congested && ! self.transmitter_is_busy
{ {
if let Some (buf_enc) = self.buf_enc.take () if let Some (buf_enc) = self.buf_enc.take ()
{ {
self.transmitter_is_busy = true;
return Some (TxPipelineEvent::Transmit (buf_enc)); return Some (TxPipelineEvent::Transmit (buf_enc));
} }
} }

View File

@ -10,17 +10,44 @@ use std::{
}; };
use rayon::ThreadPool; use rayon::ThreadPool;
use thiserror::Error;
use crate::controller::*; use crate::controller::*;
mod capture;
mod controller; mod controller;
fn main() -> Result <(), TaskError> fn main() -> Result <(), Error>
{ {
let pool = rayon::ThreadPoolBuilder::new ().build ().unwrap (); let mut args = std::env::args ();
let mut driver = Driver::new (&pool); let _exe_name = args.next ();
driver.main ()?; match args.next ().as_deref ()
{
None =>
{
let pool = rayon::ThreadPoolBuilder::new ().build ().unwrap ();
let mut driver = Driver::new (&pool)?;
driver.main ()?;
},
Some ("capture") =>
{
use std::io::Write;
let mut cap = capture::Capture::new ()?;
let mut buf = vec! [0u8; cap.size_image ()];
let mut bytesused = 0;
for _ in 0..5
{
let rc = cap.wait_for_frame (&mut buf);
bytesused = rc.unwrap ();
dbg! (bytesused);
}
let mut f = std::fs::File::create ("data.jpeg").unwrap ();
f.write_all (&buf [0..bytesused]).unwrap ();
},
Some (_) => eprintln! ("Unknown subcommand"),
}
Ok (()) Ok (())
} }
@ -50,14 +77,14 @@ struct Driver <'a>
ctl: Controller, ctl: Controller,
capture: Task <Capture, ()>, capture: Task <capture::Capture, ()>,
encoder: Task <Encoder, EncoderTaskMetadata>, encoder: Task <Encoder, EncoderTaskMetadata>,
transmitter: Task <Transmitter, ()>, transmitter: Task <Transmitter, ()>,
} }
impl <'a> Driver <'_> impl <'a> Driver <'_>
{ {
fn new (pool: &'a ThreadPool) -> Driver <'a> fn new (pool: &'a ThreadPool) -> Result <Driver <'a>, Error>
{ {
let (send, recv) = mpsc::sync_channel (8); let (send, recv) = mpsc::sync_channel (8);
@ -67,22 +94,22 @@ impl <'a> Driver <'_>
send, send,
}; };
Driver Ok (Driver
{ {
thread_sys, thread_sys,
recv, recv,
ctl: Default::default (), ctl: Default::default (),
capture: Capture::default ().into (), capture: capture::Capture::new ()?.into (),
encoder: Encoder::default ().into (), encoder: Encoder::default ().into (),
transmitter: Transmitter::default ().into (), transmitter: Transmitter::default ().into (),
} })
} }
fn main (&mut self) -> Result <(), TaskError> fn main (&mut self) -> Result <(), TaskError>
{ {
for _ in 0..10 for _ in 0..50
{ {
let ev = self.ctl.poll (); let ev = self.ctl.poll ();
@ -132,6 +159,9 @@ impl <'a> Driver <'_>
match event match event
{ {
TxPipelineEvent::Capture => CaptureWork::dispatch (self, ())?, 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 => TxPipelineEvent::PollEncoder =>
{ {
let encoder = self.encoder.try_inner_mut ()?; let encoder = self.encoder.try_inner_mut ()?;
@ -147,9 +177,7 @@ impl <'a> Driver <'_>
{ {
self.ctl.handle_encoded_packet (None).unwrap (); self.ctl.handle_encoded_packet (None).unwrap ();
} }
} },
TxPipelineEvent::Encode (buf_raw) => EncodeWork::dispatch (self, buf_raw)?,
TxPipelineEvent::Transmit (buf_enc) => TransmitWork::dispatch (self, buf_enc)?,
} }
Ok (()) Ok (())
@ -164,7 +192,7 @@ enum Task <S, R>
Running (R), Running (R),
} }
#[derive (Debug, Error)] #[derive (Debug, thiserror::Error)]
enum TaskError enum TaskError
{ {
#[error ("tried to start already-running task")] #[error ("tried to start already-running task")]
@ -245,24 +273,6 @@ impl fmt::Debug for TaskOutput {
} }
} }
/// Wraps non-thread-safe capture device like a v4l webcam
#[derive (Default)]
struct Capture
{
}
impl Capture
{
/// Blocks until the capture device gets us a frame
fn wait_for_frame (&mut self, _buf: &mut [u8]) -> bool
{
false
}
}
/// Wraps non-thread-safe encoder state like from ffmpeg /// Wraps non-thread-safe encoder state like from ffmpeg
#[derive (Default)] #[derive (Default)]
@ -352,7 +362,7 @@ impl Transmitter
struct CaptureWork struct CaptureWork
{ {
cap: Capture, cap: capture::Capture,
buf_raw: BufRaw, buf_raw: BufRaw,
} }
@ -364,7 +374,7 @@ impl CaptureWork
let mut cap = driver.capture.start (metadata)?; let mut cap = driver.capture.start (metadata)?;
driver.thread_sys.dispatch (move || driver.thread_sys.dispatch (move ||
{ {
let dur_capture = Duration::from_millis (30); let dur_capture = Duration::from_millis (1000);
thread::sleep (dur_capture); thread::sleep (dur_capture);
let buf_raw = BufRaw let buf_raw = BufRaw
{ {
@ -441,7 +451,16 @@ impl TransmitWork
fn finish (self, driver: &mut Driver) -> Result <(), TaskError> fn finish (self, driver: &mut Driver) -> Result <(), TaskError>
{ {
driver.transmitter.stop (self.tx)?; driver.transmitter.stop (self.tx)?;
driver.ctl.handle_network_busy (self.busy); driver.ctl.handle_transmitted (self.busy);
Ok (()) Ok (())
} }
} }
#[derive (Debug, thiserror::Error)]
enum Error
{
#[error ("capture")]
Capture (#[from] capture::Error),
#[error ("task")]
Task (#[from] TaskError),
}