aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-11-15 20:03:59 +0100
committermetamuffin <metamuffin@disroot.org>2023-11-15 20:03:59 +0100
commit9e1a836f663c03e462fbf1bcff444a20aaf56eaf (patch)
tree90b3fa998cdda4c7d2c22a58b42cdb5a4369f547
parent0196cc1a12cf787a4c5bfc3645b0364eb5daf93a (diff)
downloadvideo-codec-experiments-9e1a836f663c03e462fbf1bcff444a20aaf56eaf.tar
video-codec-experiments-9e1a836f663c03e462fbf1bcff444a20aaf56eaf.tar.bz2
video-codec-experiments-9e1a836f663c03e462fbf1bcff444a20aaf56eaf.tar.zst
a
-rw-r--r--Cargo.lock21
-rw-r--r--Cargo.toml1
-rw-r--r--difftree/Cargo.toml4
-rw-r--r--difftree/src/main.rs157
-rwxr-xr-xdifftree/test4
-rw-r--r--evc/src/bin/encode.rs2
-rw-r--r--evc/src/codec/decode.rs4
-rw-r--r--evc/src/codec/encode/mod.rs2
-rw-r--r--framework/src/lib.rs142
-rw-r--r--framework/src/vector.rs128
-rw-r--r--readme.md7
-rwxr-xr-xtest4
12 files changed, 360 insertions, 116 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 980b724..a470e9a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -447,6 +447,15 @@ dependencies = [
]
[[package]]
+name = "difftree"
+version = "0.1.0"
+dependencies = [
+ "framework",
+ "rand",
+ "rayon",
+]
+
+[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -554,6 +563,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
+name = "framework"
+version = "0.1.0"
+
+[[package]]
name = "futures-core"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1399,14 +1412,6 @@ dependencies = [
]
[[package]]
-name = "tweak"
-version = "0.1.0"
-dependencies = [
- "rand",
- "rayon",
-]
-
-[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index b462e8e..4245d75 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ members = [
"vgcodec",
"difftree",
"evc",
+ "framework",
"bv1/app",
"bv1/codec",
"bv1/codec-web",
diff --git a/difftree/Cargo.toml b/difftree/Cargo.toml
index 2c19efb..0aae03b 100644
--- a/difftree/Cargo.toml
+++ b/difftree/Cargo.toml
@@ -1,8 +1,10 @@
[package]
-name = "tweak"
+name = "difftree"
version = "0.1.0"
edition = "2021"
[dependencies]
rayon = "1.8.0"
rand = "0.8.5"
+
+framework = { path = "../framework" }
diff --git a/difftree/src/main.rs b/difftree/src/main.rs
index b2144a3..36faa44 100644
--- a/difftree/src/main.rs
+++ b/difftree/src/main.rs
@@ -1,40 +1,38 @@
-use rayon::join;
-use std::{
- io::{stdin, stdout, Read, Write},
- time::Instant,
+use framework::{
+ vector::{UVec2, Vec2},
+ Frame, Framework, Pixel,
};
+use rayon::join;
const WIDTH: usize = 1920;
const HEIGHT: usize = 1080;
fn main() {
- let mut oframe = Frame::new();
- let mut dframe = Frame::new();
- let mut out = Vec::<u8>::new();
- let do_debug = false;
- loop {
- let timer = Instant::now();
- let iframe = Frame::read(stdin());
+ let (mut framework, params) = Framework::init();
- let t = encode(&oframe, &iframe, Area::root());
- decode(&mut oframe, Area::root(), &t);
+ match params.mode {
+ framework::CodecMode::Encode => {
+ let mut oframe = Frame::new(params.width, params.height);
+ let mut dframe = Frame::new(params.width, params.height);
+ let mut out = Vec::<u8>::new();
+ while let Some(iframe) = framework.next_frame() {
+ let t = encode(&oframe, &iframe, Area::root());
+ decode(&mut oframe, Area::root(), &t);
- write(&mut out, &t);
+ write(&mut out, &t);
- if do_debug {
- dframe.0.copy_from_slice(&oframe.0);
- debug(&mut dframe, Area::root(), &t);
- dframe.write(stdout());
- } else {
- oframe.write(stdout());
- }
+ if params.debug > 0 {
+ dframe.pixels.copy_from_slice(&oframe.pixels);
+ debug(&mut dframe, Area::root(), &t);
+ framework.decode_done(&dframe);
+ } else {
+ framework.decode_done(&oframe)
+ }
- eprintln!(
- "compression={:.01} time={:?}",
- (WIDTH * HEIGHT * 3) as f64 / out.len() as f64,
- timer.elapsed()
- );
- out.clear();
+ out.clear();
+ }
+ }
+ framework::CodecMode::Decode => todo!(),
}
}
@@ -45,7 +43,7 @@ pub fn write(w: &mut Vec<u8>, t: &DiffTree) {
write(w, a);
write(w, b);
}
- DiffTree::Diff(d) => w.extend([1, d.0[0] as u8, d.0[1] as u8, d.0[2] as u8]),
+ DiffTree::Diff(d) => w.extend([1, d.r as u8, d.g as u8, d.b as u8]),
}
}
@@ -57,7 +55,7 @@ pub fn decode(f: &mut Frame, area: Area, tree: &DiffTree) {
decode(f, ab, tb);
}
DiffTree::Diff(diff) => {
- f.apply_area_diff(area, *diff);
+ apply_area_diff(f, area, *diff);
}
}
}
@@ -72,10 +70,7 @@ pub fn debug(f: &mut Frame, area: Area, tree: &DiffTree) {
let Area { x1, y1, x2, y2 } = area;
for x in x1..x2 {
for y in y1..y2 {
- let o = (x + y * WIDTH) * 3;
- f.0[o + 0] = 255;
- f.0[o + 1] = 0;
- f.0[o + 2] = 255;
+ f[(x, y)] = Pixel { r: 0, g: 0, b: 255 }
}
}
}
@@ -84,24 +79,31 @@ pub fn debug(f: &mut Frame, area: Area, tree: &DiffTree) {
pub fn encode(a: &Frame, b: &Frame, area: Area) -> DiffTree {
if area.area() == 1 {
- DiffTree::Diff(Frame::diff_pixel(a, b, area.x1, area.y1))
+ DiffTree::Diff(diff_pixel(
+ a,
+ b,
+ Vec2 {
+ x: area.x1,
+ y: area.y1,
+ },
+ ))
} else {
let (aa, ba) = area.split();
let (at, bt) = join(|| encode(a, b, aa), || encode(a, b, ba));
match (&at, &bt) {
(DiffTree::Diff(ad), DiffTree::Diff(bd)) => {
- let d_r = ad.0[0].abs_diff(bd.0[0]);
- let d_g = ad.0[1].abs_diff(bd.0[1]);
- let d_b = ad.0[2].abs_diff(bd.0[2]);
+ let d_r = ad.r.abs_diff(bd.r);
+ let d_g = ad.g.abs_diff(bd.g);
+ let d_b = ad.b.abs_diff(bd.b);
let visdiff = (d_r as usize + d_g as usize + d_b as usize) * aa.area();
if visdiff < 100 {
- return DiffTree::Diff(PixelDiff([
- ((ad.0[0] as i16 + bd.0[0] as i16) / 2) as i8,
- ((ad.0[1] as i16 + bd.0[1] as i16) / 2) as i8,
- ((ad.0[2] as i16 + bd.0[2] as i16) / 2) as i8,
- ]));
+ return DiffTree::Diff(Pixel {
+ r: ((ad.r as i16 + bd.r as i16) / 2) as i8,
+ g: ((ad.g as i16 + bd.g as i16) / 2) as i8,
+ b: ((ad.b as i16 + bd.b as i16) / 2) as i8,
+ });
}
}
_ => (),
@@ -112,13 +114,9 @@ pub fn encode(a: &Frame, b: &Frame, area: Area) -> DiffTree {
pub enum DiffTree {
Split([Box<DiffTree>; 2]),
- Diff(PixelDiff),
+ Diff(Pixel<i8>),
}
-#[derive(Debug)]
-pub struct Frame(Vec<u8>);
-#[derive(Debug, Clone, Copy)]
-pub struct PixelDiff([i8; 3]);
#[derive(Debug, Clone, Copy)]
pub struct Area {
x1: usize,
@@ -135,61 +133,26 @@ fn diff_clamp(x: u8, y: u8) -> i8 {
-((x - y).min(128) as i8)
}
}
-
-impl Frame {
- pub fn new() -> Self {
- Self(vec![0u8; WIDTH * HEIGHT * 3])
- }
- pub fn read(mut r: impl Read) -> Self {
- let mut f = Frame::new();
- r.read_exact(&mut f.0).unwrap();
- f
- }
- pub fn write(&self, mut w: impl Write) {
- w.write_all(&self.0).unwrap()
- }
-
- pub fn diff_pixel(a: &Frame, b: &Frame, x: usize, y: usize) -> PixelDiff {
- let o = (x + y * WIDTH) * 3;
- PixelDiff([
- diff_clamp(a.0[o + 0], b.0[o + 0]),
- diff_clamp(a.0[o + 1], b.0[o + 1]),
- diff_clamp(a.0[o + 2], b.0[o + 2]),
- ])
+pub fn diff_pixel(a: &Frame, b: &Frame, p: UVec2) -> Pixel<i8> {
+ let (ap, bp) = (a[p], b[p]);
+ Pixel {
+ r: diff_clamp(ap.r, bp.r),
+ g: diff_clamp(ap.g, bp.g),
+ b: diff_clamp(ap.b, bp.b),
}
+}
- pub fn apply_area_diff(&mut self, Area { x1, y1, x2, y2 }: Area, p: PixelDiff) {
- for x in x1..x2 {
- for y in y1..y2 {
- let o = (x + y * WIDTH) * 3;
- self.0[o + 0] = self.0[o + 0].saturating_add_signed(p.0[0]);
- self.0[o + 1] = self.0[o + 1].saturating_add_signed(p.0[1]);
- self.0[o + 2] = self.0[o + 2].saturating_add_signed(p.0[2]);
- }
+pub fn apply_area_diff(frame: &mut Frame, Area { x1, y1, x2, y2 }: Area, diff: Pixel<i8>) {
+ for x in x1..x2 {
+ for y in y1..y2 {
+ let p = &mut frame[(x, y)];
+ p.r = p.r.saturating_add_signed(diff.r);
+ p.g = p.g.saturating_add_signed(diff.g);
+ p.b = p.b.saturating_add_signed(diff.b);
}
}
- // pub fn average_area_diff(
- // &self,
- // other: &Frame,
- // area @ Area { x1, y1, x2, y2 }: Area,
- // ) -> PixelDiff {
- // let (mut r, mut g, mut b) = (0i32, 0i32, 0i32);
- // for x in x1..x2 {
- // for y in y1..y2 {
- // let o = (x + y * WIDTH) * 3;
- // r += other.0[o + 0] as i32 - self.0[o + 0] as i32;
- // g += other.0[o + 1] as i32 - self.0[o + 1] as i32;
- // b += other.0[o + 2] as i32 - self.0[o + 2] as i32;
- // }
- // }
- // let a = area.area() as i32;
- // PixelDiff([
- // (r / a).clamp(i8::MIN as i32, i8::MAX as i32) as i8,
- // (g / a).clamp(i8::MIN as i32, i8::MAX as i32) as i8,
- // (b / a).clamp(i8::MIN as i32, i8::MAX as i32) as i8,
- // ])
- // }
}
+
impl Area {
pub fn area(&self) -> usize {
self.width() as usize * self.height() as usize
diff --git a/difftree/test b/difftree/test
deleted file mode 100755
index 4bfc7d8..0000000
--- a/difftree/test
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/fish
-ffmpeg -loglevel quiet -i $argv[1] -vf 'scale=1920x1080,format=rgb24' -f rawvideo pipe:1 \
- | cargo run --release \
- | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0
diff --git a/evc/src/bin/encode.rs b/evc/src/bin/encode.rs
index 6d18992..43f2c57 100644
--- a/evc/src/bin/encode.rs
+++ b/evc/src/bin/encode.rs
@@ -3,7 +3,6 @@ use clap::Parser;
use indicatif::ProgressBar;
use libreschmux::{
codec::{
- compress::compress_block,
decode::{decode_block, DecodeConfig},
encode::{encode_block, EncodeConfig, EncodeMode},
},
@@ -12,7 +11,6 @@ use libreschmux::{
helpers::vector::Vec2,
};
use log::info;
-use rayon::{ThreadPool, ThreadPoolBuilder};
use std::io::{BufReader, BufWriter};
#[derive(Parser)]
diff --git a/evc/src/codec/decode.rs b/evc/src/codec/decode.rs
index b1f6a3d..c042278 100644
--- a/evc/src/codec/decode.rs
+++ b/evc/src/codec/decode.rs
@@ -1,7 +1,5 @@
use super::compress::lit_decompress;
-use crate::{
- block::Block, frame::Frame, helpers::threading::both_par, refsampler::Sampler, view::View,
-};
+use crate::{block::Block, frame::Frame, refsampler::Sampler, view::View};
pub struct DecodeConfig {}
diff --git a/evc/src/codec/encode/mod.rs b/evc/src/codec/encode/mod.rs
index 43607aa..30342f0 100644
--- a/evc/src/codec/encode/mod.rs
+++ b/evc/src/codec/encode/mod.rs
@@ -4,7 +4,7 @@ pub mod simple;
use crate::{
block::Block,
frame::Frame,
- helpers::{pixel::Pixel, threading::both_par, vector::Vec2},
+ helpers::{pixel::Pixel, vector::Vec2},
view::View,
};
use clap::ValueEnum;
diff --git a/framework/src/lib.rs b/framework/src/lib.rs
index 139597f..89fcc10 100644
--- a/framework/src/lib.rs
+++ b/framework/src/lib.rs
@@ -1,2 +1,144 @@
+use std::{
+ io::{self, stdin, stdout, Read, Write},
+ ops::{Index, IndexMut},
+ time::Instant,
+};
+use vector::{UVec2, Vec2};
+pub mod vector;
+#[derive(Clone)]
+pub struct CodecParams {
+ pub width: usize,
+ pub height: usize,
+ pub debug: usize,
+ pub mode: CodecMode,
+}
+#[derive(Clone)]
+pub enum CodecMode {
+ Encode,
+ Decode,
+}
+
+pub struct Framework {
+ process_start: Instant,
+ params: CodecParams,
+}
+
+impl Framework {
+ pub fn init() -> (Self, CodecParams) {
+ let width = std::env::var("V_WIDTH").unwrap().parse().unwrap();
+ let height = std::env::var("V_HEIGHT").unwrap().parse().unwrap();
+ let debug = std::env::var("V_DEBUG").unwrap().parse().unwrap();
+ let mode = match std::env::var("V_MODE").unwrap().as_str() {
+ "encode" => CodecMode::Encode,
+ "decode" => CodecMode::Decode,
+ _ => panic!("invalid mode"),
+ };
+ let params = CodecParams {
+ height,
+ width,
+ debug,
+ mode,
+ };
+ (
+ Self {
+ params: params.clone(),
+ process_start: Instant::now(),
+ },
+ params,
+ )
+ }
+
+ pub fn next_frame(&mut self) -> Option<Frame> {
+ self.process_start = Instant::now();
+ Some(Frame::read(self.params.width, self.params.height, stdin()).unwrap())
+ }
+ pub fn encode_done(&mut self, output: &[u8]) {
+ let el = self.process_start.elapsed();
+ eprintln!("size={}Kb t={el:?}", output.len() / 1000 * 8)
+ }
+
+ pub fn next_chunk(&mut self, buffer: &mut Vec<u8>) -> bool {
+ true
+ }
+ pub fn decode_done(&mut self, output: &Frame) {
+ output.write(stdout()).unwrap();
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Frame {
+ pub width: usize,
+ pub height: usize,
+ pub pixels: Vec<Pixel<u8>>,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Pixel<T> {
+ pub r: T,
+ pub g: T,
+ pub b: T,
+}
+pub type EPixel = Pixel<u8>;
+
+impl Frame {
+ pub fn new(width: usize, height: usize) -> Self {
+ Self {
+ height,
+ width,
+ pixels: vec![Pixel::BLACK; width * height],
+ }
+ }
+ pub fn read(width: usize, height: usize, mut r: impl Read) -> io::Result<Self> {
+ let mut f = Frame::new(width, height);
+ let p = unsafe {
+ std::slice::from_raw_parts_mut(
+ f.pixels.as_mut_slice().as_mut_ptr() as *mut u8,
+ width * height * 3,
+ )
+ };
+ r.read_exact(p)?;
+ Ok(f)
+ }
+ pub fn write(&self, mut w: impl Write) -> io::Result<()> {
+ let p = unsafe {
+ std::slice::from_raw_parts(
+ self.pixels.as_slice().as_ptr() as *const u8,
+ self.width * self.height * 3,
+ )
+ };
+ w.write_all(p)
+ }
+}
+
+impl Index<(usize, usize)> for Frame {
+ type Output = Pixel<u8>;
+ #[inline]
+ fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
+ &self.pixels[x + y * self.width]
+ }
+}
+impl IndexMut<(usize, usize)> for Frame {
+ #[inline]
+ fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Self::Output {
+ &mut self.pixels[x + y * self.width]
+ }
+}
+impl Index<UVec2> for Frame {
+ type Output = Pixel<u8>;
+ #[inline]
+ fn index(&self, Vec2 { x, y }: UVec2) -> &Self::Output {
+ &self.pixels[x + y * self.width]
+ }
+}
+impl IndexMut<UVec2> for Frame {
+ #[inline]
+ fn index_mut(&mut self, Vec2 { x, y }: UVec2) -> &mut Self::Output {
+ &mut self.pixels[x + y * self.width]
+ }
+}
+
+impl Pixel<u8> {
+ pub const BLACK: Pixel<u8> = Pixel { r: 0, g: 0, b: 0 };
+}
diff --git a/framework/src/vector.rs b/framework/src/vector.rs
new file mode 100644
index 0000000..5227d04
--- /dev/null
+++ b/framework/src/vector.rs
@@ -0,0 +1,128 @@
+pub type UVec2 = Vec2<usize>;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Vec2<T> {
+ pub x: T,
+ pub y: T,
+}
+
+impl From<Vec2<isize>> for Vec2<u16> {
+ fn from(value: Vec2<isize>) -> Self {
+ Self {
+ x: value.x as u16,
+ y: value.y as u16,
+ }
+ }
+}
+impl From<Vec2<u16>> for Vec2<isize> {
+ fn from(value: Vec2<u16>) -> Self {
+ Self {
+ x: value.x as isize,
+ y: value.y as isize,
+ }
+ }
+}
+impl From<Vec2<f32>> for Vec2<isize> {
+ fn from(value: Vec2<f32>) -> Self {
+ Self {
+ x: value.x as isize,
+ y: value.y as isize,
+ }
+ }
+}
+
+impl Vec2<isize> {
+ pub const ZERO: Vec2<isize> = Vec2 { x: 0, y: 0 };
+ pub const UP: Vec2<isize> = Vec2 { x: 0, y: -1 };
+ pub const LEFT: Vec2<isize> = Vec2 { x: -1, y: 0 };
+}
+impl Vec2<f32> {
+ pub const ZERO: Vec2<f32> = Vec2 { x: 0.0, y: 0.0 };
+ pub const UP: Vec2<f32> = Vec2 { x: 0.0, y: -1.0 };
+ pub const LEFT: Vec2<f32> = Vec2 { x: -1.0, y: 0.0 };
+}
+
+impl<T: std::ops::Div<Output = T> + Copy> Vec2<T> {
+ pub fn downscale(&self, f: T) -> Self {
+ Self {
+ x: self.x / f,
+ y: self.y / f,
+ }
+ }
+}
+
+impl<T: std::ops::Mul<Output = T> + Copy> Vec2<T> {
+ pub fn scale(&self, f: T) -> Self {
+ Self {
+ x: self.x * f,
+ y: self.y * f,
+ }
+ }
+ pub fn area(&self) -> T {
+ self.x * self.y
+ }
+}
+
+impl Vec2<isize> {
+ pub fn x_only(&self) -> Self {
+ Self { x: self.x, y: 0 }
+ }
+ pub fn y_only(&self) -> Self {
+ Self { x: 0, y: self.y }
+ }
+}
+
+impl Into<Vec2<f32>> for Vec2<isize> {
+ fn into(self) -> Vec2<f32> {
+ Vec2 {
+ x: self.x as f32,
+ y: self.y as f32,
+ }
+ }
+}
+impl From<(isize, isize)> for Vec2<f32> {
+ fn from((x, y): (isize, isize)) -> Self {
+ Vec2 {
+ x: x as f32,
+ y: y as f32,
+ }
+ }
+}
+
+impl<T: std::ops::Add> std::ops::Add for Vec2<T> {
+ type Output = Vec2<T::Output>;
+ #[inline]
+ fn add(self, rhs: Self) -> Self::Output {
+ Vec2 {
+ x: self.x + rhs.x,
+ y: self.y + rhs.y,
+ }
+ }
+}
+impl<T: std::ops::Sub> std::ops::Sub for Vec2<T> {
+ type Output = Vec2<T::Output>;
+ #[inline]
+ fn sub(self, rhs: Self) -> Self::Output {
+ Vec2 {
+ x: self.x - rhs.x,
+ y: self.y - rhs.y,
+ }
+ }
+}
+impl<T: std::ops::Mul> std::ops::Mul for Vec2<T> {
+ type Output = Vec2<T::Output>;
+ #[inline]
+ fn mul(self, rhs: Self) -> Self::Output {
+ Vec2 {
+ x: self.x * rhs.x,
+ y: self.y * rhs.y,
+ }
+ }
+}
+
+impl<T> From<(T, T)> for Vec2<T> {
+ #[inline]
+ fn from((x, y): (T, T)) -> Self {
+ Vec2 { x, y }
+ }
+}
diff --git a/readme.md b/readme.md
index 95b693a..a32a8a1 100644
--- a/readme.md
+++ b/readme.md
@@ -14,3 +14,10 @@ H.264.
- `vgcodec` approximates images by drawing circles (on the GPU).
- (`dhwt-codec` tries to compress using a discrete haar wavelet across all three
dimensions. that doesnt work well)
+
+## the test framework
+
+| Variable | Description |
+| ---------- | ------------ |
+| `V_WIDTH` | Video width |
+| `V_HEIGHT` | Video height |
diff --git a/test b/test
new file mode 100755
index 0000000..bba8fbb
--- /dev/null
+++ b/test
@@ -0,0 +1,4 @@
+#!/bin/fish
+ffmpeg -loglevel quiet -i $argv[2] -vf 'scale=1920x1080,format=rgb24' -f rawvideo pipe:1 \
+ | V_WIDTH=1920 V_HEIGHT=1080 V_DEBUG=0 V_MODE=encode cargo run --release --bin $argv[1] \
+ | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0