From fc881cff08422f9150717fdf53954ac6bd2697bf Mon Sep 17 00:00:00 2001 From: Dominic Date: Thu, 16 Nov 2023 09:56:34 +0100 Subject: [PATCH 01/70] try using overlay_vaapi filter --- src/render/ffmpeg.rs | 64 +++++++++++++++++++++++++++++++++++++++++--- src/render/filter.rs | 2 +- src/render/mod.rs | 21 ++++++--------- 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/render/ffmpeg.rs b/src/render/ffmpeg.rs index 614ad19..8cc8a09 100644 --- a/src/render/ffmpeg.rs +++ b/src/render/ffmpeg.rs @@ -1,6 +1,6 @@ use super::{cmd, filter::Filter}; use crate::{ - render::filter::channel, + render::filter::{channel, next_tmp}, time::{format_time, Time}, Resolution }; @@ -107,9 +107,17 @@ enum FfmpegFilter { Rescale(Resolution) } +pub(crate) struct FfmpegOverlay { + pub(crate) overlay_input: Cow<'static, str>, + pub(crate) x: Cow<'static, str>, + pub(crate) y: Cow<'static, str>, + pub(crate) alpha: Rational +} + pub(crate) struct Ffmpeg { inputs: Vec, filter: FfmpegFilter, + overlay: Option, video_bitrate: Option<&'static str>, output: FfmpegOutput, @@ -121,6 +129,7 @@ impl Ffmpeg { Self { inputs: Vec::new(), filter: FfmpegFilter::None, + overlay: None, video_bitrate: None, output, @@ -182,6 +191,11 @@ impl Ffmpeg { self } + pub fn enable_overlay(&mut self, overlay: FfmpegOverlay) -> &mut Self { + self.overlay = Some(overlay); + self + } + pub fn set_video_bitrate(&mut self, bitrate: &'static str) -> &mut Self { self.video_bitrate = Some(bitrate); self @@ -229,10 +243,52 @@ impl Ffmpeg { for filter in filters { filter.append_to_complex_filter(&mut complex, &mut self.filter_idx); } - if vaapi { - write!(complex, "{}format=nv12,hwupload[v]", channel('v', &output)); + if let Some(FfmpegOverlay { + overlay_input, + x, + y, + alpha + }) = self.overlay + { + let tmp = next_tmp(&mut self.filter_idx); + if vaapi { + writeln!( + complex, + "{}format=nv12,hwupload{tmp};", + channel('v', &output) + ); + let tmp2 = next_tmp(&mut self.filter_idx); + writeln!( + complex, + "{}format=nv12,hwupload{tmp2};", + channel('v', &overlay_input) + ); + writeln!( + complex, + "{tmp}{tmp2}overlay_vaapi=x={x}:y={y}:alpha={alpha}[v]" + ); + } else { + writeln!( + complex, + "{}format=yuva444p,colorchannelmixer=aa={alpha}{tmp};", + channel('v', &output) + ); + writeln!( + complex, + "{tmp}{}overlay=x={x}:y={y}[v]", + channel('v', &overlay_input) + ); + } } else { - write!(complex, "{}null[v]", channel('v', &output)); + if vaapi { + writeln!( + complex, + "{}format=nv12,hwupload[v]", + channel('v', &output) + ); + } else { + writeln!(complex, "{}null[v]", channel('v', &output)); + } } cmd.arg("-filter_complex").arg(complex); cmd.arg("-map").arg("[v]"); diff --git a/src/render/filter.rs b/src/render/filter.rs index 6a76ccc..fc0c720 100644 --- a/src/render/filter.rs +++ b/src/render/filter.rs @@ -234,7 +234,7 @@ pub(super) fn channel(channel: char, id: &str) -> String { } } -fn next_tmp(filter_idx: &mut usize) -> String { +pub(super) fn next_tmp(filter_idx: &mut usize) -> String { *filter_idx += 1; format!("[tmp{filter_idx}]") } diff --git a/src/render/mod.rs b/src/render/mod.rs index 3b9257c..75d440c 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -3,7 +3,10 @@ pub mod ffmpeg; mod filter; -use self::{ffmpeg::FfmpegOutput, filter::Filter}; +use self::{ + ffmpeg::{FfmpegOutput, FfmpegOverlay}, + filter::Filter +}; use crate::{ iotro::{intro, outro}, render::ffmpeg::{Ffmpeg, FfmpegInput}, @@ -426,23 +429,15 @@ impl<'a> Renderer<'a> { }); // overlay the logo - let logoalpha = "logoalpha"; - ffmpeg.add_filter(Filter::Alpha { - input: logo.into(), - alpha: 0.5, - output: logoalpha.into() - }); - let overlay = "overlay"; - ffmpeg.add_filter(Filter::Overlay { - video_input: concat.into(), - overlay_input: logoalpha.into(), + ffmpeg.enable_overlay(FfmpegOverlay { + overlay_input: logo.into(), x: "main_w-overlay_w-130".into(), y: "main_h-overlay_h-65".into(), - output: overlay.into() + alpha: Rational::new(1, 2) }); // we're done :) - ffmpeg.set_filter_output(overlay); + ffmpeg.set_filter_output(concat); ffmpeg.run()?; Ok(output) From f2f3f67d1033b4f2ab8acaf2c1d4c2951228d699 Mon Sep 17 00:00:00 2001 From: Dominic Date: Thu, 16 Nov 2023 09:57:06 +0100 Subject: [PATCH 02/70] set quality level to 22 24 hides a lot of details "in the background", i.e. in the dark green areas of the blackboard --- src/render/ffmpeg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/ffmpeg.rs b/src/render/ffmpeg.rs index 614ad19..c01269f 100644 --- a/src/render/ffmpeg.rs +++ b/src/render/ffmpeg.rs @@ -256,7 +256,7 @@ impl Ffmpeg { } // append encoding options - const QUALITY: &str = "24"; + const QUALITY: &str = "22"; if vaapi { cmd.arg("-c:v").arg("h264_vaapi"); if self.video_bitrate.is_none() { From 27c7cb3c7df850cc1874dcc7ac907f828553cccc Mon Sep 17 00:00:00 2001 From: Dominic Date: Thu, 16 Nov 2023 11:49:38 +0100 Subject: [PATCH 03/70] render videos with 3x bitrate --- src/main.rs | 12 ++++++------ src/render/ffmpeg.rs | 6 +++--- src/render/mod.rs | 9 +++++++++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index 44e034a..b978319 100644 --- a/src/main.rs +++ b/src/main.rs @@ -79,7 +79,7 @@ macro_rules! resolutions { } } - fn bitrate(self) -> &'static str { + fn bitrate(self) -> u64 { match self { $(Self::$res => $bitrate),+ } @@ -100,12 +100,12 @@ macro_rules! resolutions { } 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", + nHD: 640 x 360 at 500_000, + HD: 1280 x 720 at 1_000_000, + FullHD: 1920 x 1080 at 2_000_000, + WQHD: 2560 x 1440 at 3_000_000, // TODO qsx muss mal sagen wieviel bitrate für 4k - UHD: 3840 x 2160 at "4M" + UHD: 3840 x 2160 at 4_000_000 } #[derive(Deserialize, Serialize)] diff --git a/src/render/ffmpeg.rs b/src/render/ffmpeg.rs index c01269f..ec76a8b 100644 --- a/src/render/ffmpeg.rs +++ b/src/render/ffmpeg.rs @@ -110,7 +110,7 @@ enum FfmpegFilter { pub(crate) struct Ffmpeg { inputs: Vec, filter: FfmpegFilter, - video_bitrate: Option<&'static str>, + video_bitrate: Option, output: FfmpegOutput, filter_idx: usize @@ -182,7 +182,7 @@ impl Ffmpeg { self } - pub fn set_video_bitrate(&mut self, bitrate: &'static str) -> &mut Self { + pub fn set_video_bitrate(&mut self, bitrate: u64) -> &mut Self { self.video_bitrate = Some(bitrate); self } @@ -272,7 +272,7 @@ impl Ffmpeg { cmd.arg("-c:v").arg("copy"); } if venc && self.video_bitrate.is_some() { - cmd.arg("-b:v").arg(self.video_bitrate.unwrap()); + cmd.arg("-b:v").arg(self.video_bitrate.unwrap().to_string()); } if aenc { cmd.arg("-c:a").arg("aac"); diff --git a/src/render/mod.rs b/src/render/mod.rs index 3b9257c..2aa7398 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -443,6 +443,15 @@ impl<'a> Renderer<'a> { // we're done :) ffmpeg.set_filter_output(overlay); + ffmpeg.set_video_bitrate( + project + .source + .metadata + .as_ref() + .unwrap() + .source_res + .bitrate() * 3 + ); ffmpeg.run()?; Ok(output) From 5746939c06c46fb26bb7f16d4cf4d96b1d6bbaef Mon Sep 17 00:00:00 2001 From: Dominic Date: Thu, 16 Nov 2023 11:50:49 +0100 Subject: [PATCH 04/70] fix intro/outro producing mono audio --- src/render/filter.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/render/filter.rs b/src/render/filter.rs index 6a76ccc..4f0f84d 100644 --- a/src/render/filter.rs +++ b/src/render/filter.rs @@ -191,7 +191,11 @@ impl Filter { channel('v', video), channel('v', output) ); - writeln!(complex, "aevalsrc=0:s=48000{};", channel('a', output)); + writeln!( + complex, + "aevalsrc=0:s=48000,pan=stereo|c0=c0|c1=c0{};", + channel('a', output) + ); }, Self::FastForward { From 083bcb07c2b3466412b60efce670931ee81d2a23 Mon Sep 17 00:00:00 2001 From: Dominic Date: Thu, 16 Nov 2023 11:52:17 +0100 Subject: [PATCH 05/70] fix incorrectly setting vaapi output when copying codec --- src/render/ffmpeg.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/render/ffmpeg.rs b/src/render/ffmpeg.rs index ec76a8b..6eb642f 100644 --- a/src/render/ffmpeg.rs +++ b/src/render/ffmpeg.rs @@ -257,16 +257,18 @@ impl Ffmpeg { // append encoding options const QUALITY: &str = "22"; - if vaapi { - cmd.arg("-c:v").arg("h264_vaapi"); - if self.video_bitrate.is_none() { - cmd.arg("-rc_mode").arg("CQP"); - cmd.arg("-global_quality").arg(QUALITY); - } - } else if venc { - cmd.arg("-c:v").arg("libx264"); - if self.video_bitrate.is_none() { - cmd.arg("-crf").arg(QUALITY); + if venc { + if vaapi { + cmd.arg("-c:v").arg("h264_vaapi"); + if self.video_bitrate.is_none() { + cmd.arg("-rc_mode").arg("CQP"); + cmd.arg("-global_quality").arg(QUALITY); + } + } else { + cmd.arg("-c:v").arg("libx264"); + if self.video_bitrate.is_none() { + cmd.arg("-crf").arg(QUALITY); + } } } else { cmd.arg("-c:v").arg("copy"); From fad0d1ec6c57b5d99e2346077ac9f86769f5be84 Mon Sep 17 00:00:00 2001 From: Dominic Date: Thu, 16 Nov 2023 12:05:13 +0100 Subject: [PATCH 06/70] optimize if chain --- src/render/ffmpeg.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/render/ffmpeg.rs b/src/render/ffmpeg.rs index 6eb642f..f1e4a2a 100644 --- a/src/render/ffmpeg.rs +++ b/src/render/ffmpeg.rs @@ -270,12 +270,12 @@ impl Ffmpeg { cmd.arg("-crf").arg(QUALITY); } } + if self.video_bitrate.is_some() { + cmd.arg("-b:v").arg(self.video_bitrate.unwrap().to_string()); + } } else { cmd.arg("-c:v").arg("copy"); } - if venc && self.video_bitrate.is_some() { - cmd.arg("-b:v").arg(self.video_bitrate.unwrap().to_string()); - } if aenc { cmd.arg("-c:a").arg("aac"); cmd.arg("-b:a").arg("128000"); From 27e986d53b1794480b24af942b4678788f1aa30d Mon Sep 17 00:00:00 2001 From: Dominic Date: Thu, 16 Nov 2023 12:12:17 +0100 Subject: [PATCH 07/70] fix warnings --- src/main.rs | 8 ++-- src/render/ffmpeg.rs | 7 +-- src/render/filter.rs | 105 +++++++++---------------------------------- src/render/mod.rs | 5 +-- src/time.rs | 6 +-- 5 files changed, 33 insertions(+), 98 deletions(-) diff --git a/src/main.rs b/src/main.rs index b978319..dcf9acd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ #![allow(clippy::manual_range_contains)] -#![warn(rust_2018_idioms)] +#![warn(clippy::unreadable_literal, rust_2018_idioms)] #![forbid(elided_lifetimes_in_paths, unsafe_code)] mod iotro; @@ -281,7 +281,9 @@ fn main() { // render the video let mut videos = Vec::new(); - videos.push(if !project.progress.rendered { + videos.push(if project.progress.rendered { + renderer.video_mp4(&project) + } else { let video = renderer.render(&mut project).unwrap(); project.progress.rendered = true; @@ -289,8 +291,6 @@ fn main() { fs::write(&project_path, toml::to_string(&project).unwrap().as_bytes()).unwrap(); video - } else { - renderer.video_mp4(&project) }); // rescale the video diff --git a/src/render/ffmpeg.rs b/src/render/ffmpeg.rs index f1e4a2a..2e2d174 100644 --- a/src/render/ffmpeg.rs +++ b/src/render/ffmpeg.rs @@ -227,12 +227,13 @@ impl Ffmpeg { FfmpegFilter::Filters { filters, output } => { let mut complex = String::new(); for filter in filters { - filter.append_to_complex_filter(&mut complex, &mut self.filter_idx); + filter + .append_to_complex_filter(&mut complex, &mut self.filter_idx)?; } if vaapi { - write!(complex, "{}format=nv12,hwupload[v]", channel('v', &output)); + write!(complex, "{}format=nv12,hwupload[v]", channel('v', &output))?; } else { - write!(complex, "{}null[v]", channel('v', &output)); + write!(complex, "{}null[v]", channel('v', &output))?; } cmd.arg("-filter_complex").arg(complex); cmd.arg("-map").arg("[v]"); diff --git a/src/render/filter.rs b/src/render/filter.rs index 4f0f84d..dd82ecb 100644 --- a/src/render/filter.rs +++ b/src/render/filter.rs @@ -1,15 +1,11 @@ -use crate::time::{format_time, Time}; -use std::{borrow::Cow, collections::VecDeque, fmt::Write as _}; +use crate::time::Time; +use std::{ + borrow::Cow, + collections::VecDeque, + fmt::{self, Write as _} +}; pub(crate) enum Filter { - /// Trim audio and video alike - Trim { - input: Cow<'static, str>, - start: Option