use std::collections::HashMap;
use std::sync::Mutex;
use anyhow::{Error, Result, anyhow};
use av_denoise_core::{FrameLayout, PlanarDenoiser, Planes, WarmUp, WindowSpan};
use vapoursynth::core::CoreRef;
use vapoursynth::plugins::{Filter, FrameContext};
use vapoursynth::prelude::{API, FrameRef, FrameRefMut, Node, Property};
use vapoursynth::video_info::{Resolution, VideoInfo};
use crate::frames::{pack_plane, unpack_plane_into, window_indices};
use crate::params::{AlgorithmKind, RawFormat, RawParams, layout_from_format, plane_options_from};
struct State {
denoiser: PlanarDenoiser,
last: Option<usize>,
warm_up: Option<WarmUp>,
}
impl State {
fn finish_warm_up(&mut self) {
if let Some(warm_up) = self.warm_up.take() {
warm_up.finish();
}
}
}
pub struct Denoise<'core> {
source: Node<'core>,
layout: FrameLayout,
span: WindowSpan,
source_len: usize,
state: Mutex<State>,
}
impl<'core> Denoise<'core> {
pub(crate) fn create(
_api: API,
_core: CoreRef<'core>,
source: Node<'core>,
algorithm_kind: AlgorithmKind,
raw: &RawParams,
) -> Result<Self, Error> {
unsafe { av_denoise_core::raise_codegen_stack_limit() };
let info = source.info();
let (width, height) = match info.resolution {
Property::Constant(res) => (res.width as u32, res.height as u32),
Property::Variable => {
anyhow::bail!("clips with variable resolution are not supported");
},
};
let format = info.format;
let raw_format = RawFormat {
sample_type: format.sample_type(),
bits_per_sample: format.bits_per_sample(),
subsampling_w: format.sub_sampling_w(),
subsampling_h: format.sub_sampling_h(),
color_family: format.color_family(),
};
let layout = layout_from_format(raw_format, width, height)?;
let plane_options = plane_options_from(raw, algorithm_kind, layout)?;
av_denoise_core::install_compilation_cache_once();
let warm_up = WarmUp::begin(av_denoise_core::kernel_key(&plane_options, layout));
let denoiser = PlanarDenoiser::create(&plane_options, layout)?;
let span = denoiser.window_span();
Ok(Self {
source,
layout,
span,
source_len: info.num_frames,
state: Mutex::new(State {
denoiser,
last: None,
warm_up,
}),
})
}
fn window(&self, n: usize) -> Vec<usize> {
window_indices(n, self.span.behind, self.span.ahead, self.source_len - 1)
}
fn unique_window(&self, n: usize) -> Vec<usize> {
let mut indices = self.window(n);
indices.sort_unstable();
indices.dedup();
indices
}
fn render(&self, n: usize, fetch: impl Fn(usize) -> Result<Planes, Error>) -> Result<Planes, Error> {
let mut state = self.state.lock().expect("denoiser mutex poisoned");
let last_frame = self.source_len - 1;
let sequential = state.last == Some(n.wrapping_sub(1)) && n > 0;
state.last = None;
if sequential {
let ahead = (n + self.span.ahead).min(last_frame);
state.denoiser.push(&fetch(ahead)?)?;
if let Some(out) = state.denoiser.recv()? {
state.last = Some(n);
state.finish_warm_up();
return Ok(out);
}
}
let window: Vec<Planes> = self.window(n).into_iter().map(fetch).collect::<Result<_, _>>()?;
let out = state.denoiser.reseed(&window)?;
state.last = Some(n);
state.finish_warm_up();
Ok(out)
}
}
fn pack_frame(frame: &FrameRef, depth_bytes: usize) -> Planes {
let pack = |plane: usize| -> Vec<u8> {
let stride = frame.stride(plane);
let height = frame.height(plane);
let width_bytes = frame.width(plane) * depth_bytes;
let data = unsafe { std::slice::from_raw_parts(frame.data_ptr(plane), stride * height) };
pack_plane(data, stride, width_bytes, height)
};
Planes {
y: pack(0),
u: pack(1),
v: pack(2),
}
}
fn unpack_into_frame(frame: &mut FrameRefMut, planes: &Planes, depth_bytes: usize) {
let sources = [&planes.y, &planes.u, &planes.v];
for (plane, src) in sources.into_iter().enumerate() {
let stride = frame.stride(plane);
let height = frame.height(plane);
let width_bytes = frame.width(plane) * depth_bytes;
let data = unsafe { std::slice::from_raw_parts_mut(frame.data_ptr_mut(plane), stride * height) };
unpack_plane_into(data, stride, width_bytes, height, src);
}
}
impl<'core> Filter<'core> for Denoise<'core> {
fn video_info(&self, _api: API, _core: CoreRef<'core>) -> Vec<VideoInfo<'core>> {
vec![self.source.info()]
}
fn get_frame_initial(
&self,
_api: API,
_core: CoreRef<'core>,
context: FrameContext,
n: usize,
) -> Result<Option<FrameRef<'core>>, Error> {
for idx in self.unique_window(n) {
self.source.request_frame_filter(context, idx);
}
Ok(None)
}
fn get_frame(
&self,
_api: API,
core: CoreRef<'core>,
context: FrameContext,
n: usize,
) -> Result<FrameRef<'core>, Error> {
let mut frames: HashMap<usize, FrameRef<'core>> = HashMap::new();
for idx in self.unique_window(n) {
let frame = self
.source
.get_frame_filter(context, idx)
.ok_or_else(|| anyhow!("couldn't get source frame {idx}"))?;
frames.insert(idx, frame);
}
let depth_bytes = self.layout.depth.bytes_per_sample();
let fetch = |idx: usize| -> Result<Planes, Error> {
let frame = frames
.get(&idx)
.expect("get_frame_initial requested the same window as get_frame");
Ok(pack_frame(frame, depth_bytes))
};
let planes = self.render(n, fetch)?;
let prop_src = frames.get(&n).expect("the window always includes n");
let format = prop_src.format();
let resolution = Resolution {
width: self.layout.width as usize,
height: self.layout.height as usize,
};
let mut out = unsafe { FrameRefMut::new_uninitialized(core, Some(prop_src), format, resolution) };
unpack_into_frame(&mut out, &planes, depth_bytes);
Ok(out.into())
}
}