From dbdd5e089d89d27d3e8e1a1508df8b6d7b753960 Mon Sep 17 00:00:00 2001 From: Josh Holmer Date: Tue, 21 May 2024 12:25:13 -0400 Subject: [PATCH] Implement zerocopy scene detection for Vapoursynth inputs --- Cargo.lock | 5 ++- av1an-core/Cargo.toml | 4 +- av1an-core/src/scene_detect.rs | 70 ++++++++++++++++++---------------- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79123b67..b39e1a28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,12 +206,13 @@ dependencies = [ [[package]] name = "av-scenechange" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449c6f8e61df7df762ec08db17e271d8343f4d432035d7723d8475c06563d738" +checksum = "470112491badcebb8f4af5788fb04e535b2b4ed46640bdb473732d4ebbaa7c59" dependencies = [ "anyhow", "rav1e", + "vapoursynth", "y4m", ] diff --git a/av1an-core/Cargo.toml b/av1an-core/Cargo.toml index 6a1480f2..1cfebebf 100644 --- a/av1an-core/Cargo.toml +++ b/av1an-core/Cargo.toml @@ -40,7 +40,9 @@ crossbeam-channel = "0.5.1" crossbeam-utils = "0.8.5" textwrap = "0.16.0" path_abs = "0.5.1" -av-scenechange = { version = "0.10.0", default-features = false } +av-scenechange = { version = "0.11.0", default-features = false, features = [ + "vapoursynth", +] } y4m = "0.8.0" thiserror = "1.0.30" paste = "1.0.5" diff --git a/av1an-core/src/scene_detect.rs b/av1an-core/src/scene_detect.rs index 4f53e454..651efddb 100644 --- a/av1an-core/src/scene_detect.rs +++ b/av1an-core/src/scene_detect.rs @@ -4,6 +4,8 @@ use std::thread; use ansi_term::Style; use anyhow::bail; +use av_scenechange::decoder::Decoder; +use av_scenechange::vapoursynth::VapoursynthDecoder; use av_scenechange::{detect_scene_changes, DetectionOptions, SceneDetectionSpeed}; use ffmpeg::format::Pixel; use itertools::Itertools; @@ -147,7 +149,7 @@ pub fn scene_detect( frame_limit, callback.as_ref().map(|cb| cb as &dyn Fn(usize, usize)), ) - }; + }?; if let Some(limit) = frame_limit { if limit != sc_result.frame_count { bail!( @@ -206,7 +208,7 @@ fn build_decoder( sc_scaler: &str, sc_pix_format: Option, sc_downscale_height: Option, -) -> anyhow::Result<(y4m::Decoder, usize)> { +) -> anyhow::Result<(Decoder, usize)> { let bit_depth; let filters: SmallVec<[String; 4]> = match (sc_downscale_height, sc_pix_format) { (Some(sdh), Some(spf)) => into_smallvec![ @@ -225,53 +227,57 @@ fn build_decoder( (None, None) => smallvec![], }; - let decoder = y4m::Decoder::new(match input { + let decoder = match input { Input::VapourSynth(path) => { bit_depth = crate::vapoursynth::bit_depth(path.as_ref())?; - let vspipe = Command::new("vspipe") - .arg("-c") - .arg("y4m") - .arg(path) - .arg("-") - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(Stdio::null()) - .spawn()? - .stdout - .unwrap(); if !filters.is_empty() { - Command::new("ffmpeg") - .stdin(vspipe) - .args(["-i", "pipe:", "-f", "yuv4mpegpipe", "-strict", "-1"]) - .args(filters) + let vspipe = Command::new("vspipe") + .arg("-c") + .arg("y4m") + .arg(path) .arg("-") + .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::null()) .spawn()? .stdout - .unwrap() + .unwrap(); + Decoder::Y4m(y4m::Decoder::new( + Command::new("ffmpeg") + .stdin(vspipe) + .args(["-i", "pipe:", "-f", "yuv4mpegpipe", "-strict", "-1"]) + .args(filters) + .arg("-") + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn()? + .stdout + .unwrap(), + )?) } else { - vspipe + Decoder::Vapoursynth(VapoursynthDecoder::new(path.as_ref())?) } } Input::Video(path) => { let input_pix_format = crate::ffmpeg::get_pixel_format(path.as_ref()) .unwrap_or_else(|e| panic!("FFmpeg failed to get pixel format for input video: {e:?}")); bit_depth = encoder.get_format_bit_depth(sc_pix_format.unwrap_or(input_pix_format))?; - Command::new("ffmpeg") - .args(["-r", "1", "-i"]) - .arg(path) - .args(filters.as_ref()) - .args(["-f", "yuv4mpegpipe", "-strict", "-1", "-"]) - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(Stdio::null()) - .spawn()? - .stdout - .unwrap() + Decoder::Y4m(y4m::Decoder::new( + Command::new("ffmpeg") + .args(["-r", "1", "-i"]) + .arg(path) + .args(filters.as_ref()) + .args(["-f", "yuv4mpegpipe", "-strict", "-1", "-"]) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn()? + .stdout + .unwrap(), + )?) } - })?; + }; Ok((decoder, bit_depth)) }