aboutsummaryrefslogtreecommitdiff
path: root/lvc/app/src/bin/main.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-03-09 18:16:05 +0100
committermetamuffin <metamuffin@disroot.org>2023-03-09 18:16:05 +0100
commitac27a58c87bea9480f6603a731448f69c9c3b0fb (patch)
treeb437fb947a5d650c88d8db743c521e832f79611f /lvc/app/src/bin/main.rs
parent4fbf4bb4310da4bede43c9428809ac9a8804982a (diff)
downloadvideo-codec-experiments-ac27a58c87bea9480f6603a731448f69c9c3b0fb.tar
video-codec-experiments-ac27a58c87bea9480f6603a731448f69c9c3b0fb.tar.bz2
video-codec-experiments-ac27a58c87bea9480f6603a731448f69c9c3b0fb.tar.zst
rearrange files
Diffstat (limited to 'lvc/app/src/bin/main.rs')
-rw-r--r--lvc/app/src/bin/main.rs184
1 files changed, 184 insertions, 0 deletions
diff --git a/lvc/app/src/bin/main.rs b/lvc/app/src/bin/main.rs
new file mode 100644
index 0000000..79869c3
--- /dev/null
+++ b/lvc/app/src/bin/main.rs
@@ -0,0 +1,184 @@
+use bv1::{
+ debug::draw_debug,
+ decode::decode,
+ encode::{encode, EncodeConfig},
+ huff::{read_huff, write_huff},
+ Block, Frame, Pixel, PixelValue, View, P2,
+};
+use clap::{Parser, Subcommand};
+use std::{
+ io::{stdin, stdout, BufReader, BufWriter, Read, Write},
+ time::Instant,
+};
+
+#[derive(Parser)]
+#[clap(about, version)]
+struct Args {
+ // Width of the video signal
+ #[arg(short = 'W', long)]
+ width: u16,
+ // Height of the video signal
+ #[arg(short = 'H', long)]
+ height: u16,
+ #[clap(subcommand)]
+ action: Action,
+}
+
+#[derive(Clone, Subcommand)]
+enum Action {
+ // Compress video
+ Encode {
+ #[arg(short, long, default_value_t = 800)]
+ max_block_size: usize,
+ #[arg(short, long, default_value_t = 10_000)]
+ attention_split: u32,
+ #[arg(short, long, default_value_t = 10.)]
+ threshold: f32,
+ #[arg(short, long, default_value_t = 10)]
+ keyframe_interval: usize,
+ },
+ // Decompress video
+ Decode {
+ #[arg(short, long)]
+ debug: bool,
+ },
+}
+
+fn main() {
+ let args = Args::parse();
+
+ let size = P2 {
+ x: args.width as i32,
+ y: args.height as i32,
+ };
+ match args.action {
+ Action::Encode {
+ max_block_size,
+ threshold,
+ attention_split,
+ keyframe_interval,
+ } => {
+ let config = EncodeConfig {
+ threshold,
+ max_block_size,
+ attention_split,
+ };
+ let mut last_frame = Frame::new(size);
+
+ let mut stdin = BufReader::new(stdin());
+ let mut stdout = BufWriter::new(stdout());
+
+ let huff = true;
+
+ for frame_number in 0.. {
+ let mut frame = read_frame(&mut stdin, size);
+
+ let mut config = config.clone();
+ if frame_number % keyframe_interval != 0 {
+ config.threshold = std::f32::INFINITY;
+ }
+
+ let t = Instant::now();
+ let b: Block = encode(&last_frame, &frame, View::all(size), &config);
+ let time_encode = t.elapsed();
+
+ let t = Instant::now();
+ decode(&last_frame, &mut frame, View::all(size), &b);
+ last_frame = frame;
+ let time_decode = t.elapsed();
+
+ if huff {
+ let mut buf = vec![];
+ let mut bufw = std::io::Cursor::new(&mut buf);
+ b.write(&mut bufw).unwrap();
+ drop(bufw);
+ let t = Instant::now();
+ let bits_raw = buf.len() * 8;
+ let bits_huff = write_huff(&buf, &mut stdout).unwrap();
+ let time_huff = t.elapsed();
+ drop(buf);
+
+ eprintln!(
+ "frame {frame_number}: {:?}",
+ time_decode + time_huff + time_encode
+ );
+ eprintln!(
+ "\tencode {time_encode:?} ({:.2}%)",
+ (bits_raw as f32 / (size.area() * 24) as f32) * 100.0
+ );
+ eprintln!(
+ "\thuff {time_huff:?} ({:.2}%)",
+ (bits_huff as f32 / bits_raw as f32) * 100.0
+ );
+ eprintln!("\tdecode {time_decode:?}");
+ } else {
+ b.write(&mut stdout).unwrap();
+ }
+ }
+ }
+ Action::Decode { debug } => {
+ let mut frame = Frame::new(size);
+ let mut last_frame = Frame::new(size);
+ let mut debug_frame = if debug { Some(Frame::new(size)) } else { None };
+
+ let mut stdin = BufReader::new(stdin());
+ let mut stdout = BufWriter::new(stdout());
+
+ let huff = true;
+
+ loop {
+ let b = if huff {
+ let mut buf = vec![];
+ read_huff(&mut stdin, &mut buf).unwrap();
+ let mut buf = std::io::Cursor::new(&mut buf);
+ Block::read(&mut buf, View::all(size)).unwrap()
+ } else {
+ Block::read(&mut stdin, View::all(size)).unwrap()
+ };
+
+ decode(&last_frame, &mut frame, View::all(size), &b);
+
+ if let Some(debug_frame) = &mut debug_frame {
+ debug_frame.pixels.copy_from_slice(&frame.pixels);
+ draw_debug(debug_frame, View::all(size), &b);
+ write_frame(&mut stdout, &debug_frame);
+ } else {
+ write_frame(&mut stdout, &frame);
+ }
+
+ last_frame.pixels.copy_from_slice(&frame.pixels); // TODO use mem::swap
+ frame.pixels.iter_mut().for_each(|e| *e = Pixel::BLACK);
+ }
+ }
+ }
+}
+
+fn read_frame(inp: &mut impl Read, size: P2) -> Frame {
+ let mut f = Frame::new(size);
+
+ for y in 0..size.y {
+ for x in 0..size.x {
+ let mut cc = [0u8; 3];
+ inp.read_exact(&mut cc).unwrap();
+ f[P2 { x, y }] = Pixel {
+ r: cc[0] as PixelValue,
+ g: cc[1] as PixelValue,
+ b: cc[2] as PixelValue,
+ };
+ }
+ }
+ f
+}
+
+fn write_frame(out: &mut impl Write, frame: &Frame) {
+ for y in 0..frame.size.y {
+ for x in 0..frame.size.x {
+ let p = frame[P2 { x, y }];
+ let mut cc = [0u8; 3];
+ cc[0] = p.r.clamp(0, 255) as u8;
+ cc[1] = p.g.clamp(0, 255) as u8;
+ cc[2] = p.b.clamp(0, 255) as u8;
+ out.write_all(&mut cc).unwrap();
+ }
+ }
+}