use std::{sync::{mpsc::{Receiver}, Arc, Mutex}, path::Path};
use av_metrics_decoders::{Frame, Pixel, Decoder, Plane, VapoursynthDecoder};
#[inline(always)]
fn round_power_of_two(value: isize, n: isize) -> isize {
((value) + ((1 << (n)) >> 1)) >> (n)
}
#[inline(always)]
fn run_plane<D: Pixel>(plane: &Plane<D>, depth: usize) -> f64 {
let threshold = 50;
let (width, height, stride) =
(plane.cfg.width, plane.cfg.height, plane.cfg.stride);
let (mut count, mut accum) = (0, 0);
let plane_data = plane.data_origin();
for i in 1..height-1 {
for j in 1..width-1 {
let mut mat: [[isize; 3]; 3] = Default::default();
let center_idx = i * stride + j;
for ii in -1..=1_isize {
for jj in -1..=1_isize {
let idx = center_idx + (ii as usize) * stride + (jj as usize);
mat[(ii + 1) as usize][(jj + 1) as usize] = plane_data[idx].to_isize().unwrap();
}
}
let g_x = (mat[0][0] - mat[0][2]) + (mat[2][0] - mat[2][2]) +
2 * (mat[1][0] - mat[1][2]);
let g_y = (mat[0][0] - mat[2][0]) + (mat[0][2] - mat[2][2]) +
2 * (mat[0][1] - mat[2][1]);
let g_a = round_power_of_two(g_x.abs() + g_y.abs(), depth as isize - 8);
if g_a < threshold { let v = 4 * mat[1][1] -
2 * (mat[0][1] + mat[2][1] + mat[1][0] + mat[1][2]) +
(mat[0][0] + mat[0][2] + mat[2][0] + mat[2][2]);
accum += round_power_of_two(v.abs(), depth as isize - 8);
count += 1;
}
}
}
if count < 16 { -1.0 } else {
accum as f64 / (6.0 * count as f64) * 1.25331413732
}
}
fn run_frame<D: Pixel>(frame: &Frame<D>, depth: usize) -> [f64; 3] {
let planes = &frame.planes;
let mut scores: [f64; 3] = [-1.0, -1.0, -1.0];
scores[0] = run_plane(&planes[0], depth);
scores[1] = run_plane(&planes[1], depth);
scores[2] = run_plane(&planes[2], depth);
scores
}
pub fn run(source: &Path, threads: usize) -> (Option<usize>, Receiver<[f64; 3]>) {
let decoder = VapoursynthDecoder::new_from_video(source)
.expect("Failed to open input file!");
let frame_count = decoder.get_frame_count();
let depth = decoder.get_bit_depth();
let decoders = Arc::new(Mutex::new(decoder));
let (tx, rx) = std::sync::mpsc::channel();
for _ in 0..threads {
let decoders = Arc::clone(&decoders);
let res_tx = tx.clone();
std::thread::spawn(move || {
match depth {
8 => {
loop {
let frame = {
let mut guard = decoders.lock().unwrap();
guard.read_video_frame::<u8>()
};
match frame {
Some(f) => {
res_tx.send(run_frame(&f, 8)).unwrap();
}
None => {
break;
},
}
}
},
_ => {
loop {
let frame = {
let mut guard = decoders.lock().unwrap();
guard.read_video_frame::<u16>()
};
match frame {
Some(f) => {
res_tx.send(run_frame(&f, depth)).unwrap();
}
None => {
break;
},
}
}
}
}
});
}
drop(tx);
(frame_count.ok(), rx)
}