From feb8596bfc31621cf8a3dd1c44d95929be686eb2 Mon Sep 17 00:00:00 2001 From: Dominic Date: Fri, 3 Nov 2023 10:02:30 +0100 Subject: [PATCH] support rescaling --- src/main.rs | 117 ++++++++++++++++++++++++++++++++-------------- src/render/mod.rs | 54 ++++++++++++++++----- 2 files changed, 125 insertions(+), 46 deletions(-) diff --git a/src/main.rs b/src/main.rs index 04e5791..8df5f65 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,41 +37,56 @@ struct Args { mem_limit: String } -#[allow(non_camel_case_types, clippy::upper_case_acronyms)] -#[derive(Clone, Copy, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] -enum Resolution { - /// 640x360 - nHD, - /// 1280x720 - HD, - /// 1920x1080 - FullHD, - /// 2560x1440 - WQHD, - /// 3840x2160 - UHD +macro_rules! resolutions { + ($($res:ident: $width:literal x $height:literal at $bitrate:literal),+) => { + #[allow(non_camel_case_types, clippy::upper_case_acronyms)] + #[derive(Clone, Copy, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] + enum Resolution { + $( + #[doc = concat!(stringify!($width), "x", stringify!($height))] + $res + ),+ + } + + const NUM_RESOLUTIONS: usize = { + let mut num = 0; + $(num += 1; stringify!($res);)+ + num + }; + + impl Resolution { + fn values() -> [Self; NUM_RESOLUTIONS] { + [$(Self::$res),+] + } + + fn width(self) -> usize { + match self { + $(Self::$res => $width),+ + } + } + + fn height(self) -> usize { + match self { + $(Self::$res => $height),+ + } + } + + fn bitrate(self) -> &'static str { + match self { + $(Self::$res => $bitrate),+ + } + } + } + } } -impl Resolution { - fn width(self) -> usize { - match self { - Self::nHD => 640, - Self::HD => 1280, - Self::FullHD => 1920, - Self::WQHD => 2560, - Self::UHD => 3840 - } - } - - fn height(self) -> usize { - match self { - Self::nHD => 360, - Self::HD => 720, - Self::FullHD => 1080, - Self::WQHD => 1440, - Self::UHD => 2160 - } - } +resolutions! { + nHD: 640 x 360 at "500k", + HD: 1280 x 720 at "1M", + FullHD: 1920 x 1080 at "2M", + WQHD: 2560 x 1440 at "3M", + // TODO qsx muss mal sagen wieviel bitrate für 4k + UHD: 3840 x 2160 at "4M" } #[derive(Deserialize, Serialize)] @@ -245,6 +260,38 @@ fn main() { fs::write(&project_path, toml::to_string(&project).unwrap().as_bytes()).unwrap(); } - let video = renderer.render(&mut project).unwrap(); - println!("\x1B[1m ==> DONE :)\x1B[0m Video: {video}"); + // render the video + let mut videos = Vec::new(); + videos.push(if !project.progress.rendered { + let video = renderer.render(&mut project).unwrap(); + project.progress.rendered = true; + + println!("{}", toml::to_string(&project).unwrap()); + fs::write(&project_path, toml::to_string(&project).unwrap().as_bytes()).unwrap(); + + video + } else { + renderer.video_mp4(&project) + }); + + // rescale the video + for res in Resolution::values() { + if res >= project.source.metadata.as_ref().unwrap().source_res { + continue; + } + if !project.progress.transcoded.contains(&res) { + videos.push(renderer.rescale(res, &project).unwrap()); + project.progress.transcoded.insert(res); + + println!("{}", toml::to_string(&project).unwrap()); + fs::write(&project_path, toml::to_string(&project).unwrap().as_bytes()) + .unwrap(); + } + } + + println!("\x1B[1m ==> DONE :)\x1B[0m"); + println!(" Videos:"); + for v in &videos { + println!(" -> {v}"); + } } diff --git a/src/render/mod.rs b/src/render/mod.rs index 56e6354..49c62b8 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -261,18 +261,17 @@ impl<'a> Renderer<'a> { Ok(()) } + fn video_mp4_res(&self, res: Resolution) -> PathBuf { + self.target + .join(format!("{}-{}p.mp4", self.slug, res.height())) + } + + pub(crate) fn video_mp4(&self, project: &Project) -> PathBuf { + self.video_mp4_res(project.source.metadata.as_ref().unwrap().source_res) + } + pub(crate) fn render(&self, project: &mut Project) -> anyhow::Result { - let mut output = self.target.join(format!( - "{}-{}p.mp4", - self.slug, - project - .source - .metadata - .as_ref() - .unwrap() - .source_res - .height() - )); + let output = self.video_mp4(project); let mut ffmpeg = Ffmpeg::new(output.clone()); // add all of our inputs @@ -435,4 +434,37 @@ impl<'a> Renderer<'a> { Ok(output) } + + pub fn rescale(&self, res: Resolution, project: &Project) -> anyhow::Result { + let input = self.video_mp4(project); + let output = self.video_mp4_res(res); + println!("\x1B[1m ==> Rescaling to {}p\x1B[0m", res.height()); + + let mut ffmpeg = cmd(); + ffmpeg.arg("ffmpeg").arg("-hide_banner"); + // TODO do we just always want hwaccel? + ffmpeg + .arg("-hwaccel") + .arg("vaapi") + .arg("-hwaccel_device") + .arg("/dev/dri/renderD128") + .arg("-hwaccel_output_format") + .arg("vaapi"); + ffmpeg.arg("-i").arg(input); + ffmpeg.arg("-vf").arg(format!( + "scale_vaapi=w={}:h={}", + res.width(), + res.height() + )); + ffmpeg.arg("-c:a").arg("copy").arg("-c:v").arg("h264_vaapi"); + ffmpeg.arg("-b:v").arg(res.bitrate()); + ffmpeg.arg(&output); + + let status = ffmpeg.status()?; + if status.success() { + Ok(output) + } else { + bail!("ffmpeg failed with exit code {:?}", status.code()) + } + } }