use std::io::{stdin, stdout};
use av_denoise::DenoiserError;
use crate::ingest::{
CliOptions,
FrameLayout,
Planes,
WorkerDenoiser,
subsampling_from_y4m,
subsampling_to_y4m,
};
pub fn run_stdin(opts: &CliOptions) -> Result<(), anyhow::Error> {
let stdin = stdin();
let mut decoder = y4m::Decoder::new(stdin.lock())?;
if decoder.get_bytes_per_sample() != 1 {
anyhow::bail!(
"only 8-bit y4m input is supported (got bit depth {})",
decoder.get_bit_depth()
);
}
let layout = FrameLayout {
width: decoder.get_width() as u32,
height: decoder.get_height() as u32,
subsampling: subsampling_from_y4m(decoder.get_colorspace())?,
};
let framerate = decoder.get_framerate();
let pixel_aspect = decoder.get_pixel_aspect();
let colorspace = subsampling_to_y4m(layout.subsampling);
let stdout = stdout();
let mut encoder = y4m::encode(layout.width as usize, layout.height as usize, framerate)
.with_colorspace(colorspace)
.with_pixel_aspect(pixel_aspect)
.write_header(stdout.lock())?;
let mut wd = WorkerDenoiser::create(opts, layout)?;
tracing::info!(
accelerator = ?opts.accelerators,
width = layout.width,
height = layout.height,
subsampling = ?layout.subsampling,
"stdin pipeline ready",
);
loop {
let frame = match decoder.read_frame() {
Ok(f) => f,
Err(y4m::Error::EOF) => break,
Err(e) => return Err(e.into()),
};
let planes = Planes {
y: frame.get_y_plane().to_vec(),
u: frame.get_u_plane().to_vec(),
v: frame.get_v_plane().to_vec(),
};
if let Err(DenoiserError::QueueFull) = wd.push(&planes) {
if let Some(out) = wd.recv()? {
write_planes(&mut encoder, &out)?;
}
wd.push(&planes)?;
}
if let Some(out) = wd.recv()? {
write_planes(&mut encoder, &out)?;
}
}
wd.flush(|out| {
if let Err(e) = write_planes(&mut encoder, &out) {
tracing::error!(error = ?e, "failed to write flushed frame");
}
})?;
Ok(())
}
fn write_planes(
encoder: &mut y4m::Encoder<std::io::StdoutLock<'_>>,
planes: &Planes,
) -> Result<(), anyhow::Error> {
let frame = y4m::Frame::new([&planes.y, &planes.u, &planes.v], None);
encoder.write_frame(&frame)?;
Ok(())
}