diff --git a/src/main.rs b/src/main.rs index a6715b699b1fb612732db500fe3bf3312cb7f694..52e85653647701e78746f76dafd6627ba10e26b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,12 +37,17 @@ struct Args { course: String, /// The memory limit for external tools like ffmpeg. - #[clap(short, long, default_value = "8G")] + #[clap(short, long, default_value = "12G")] mem_limit: String, /// Transcode the final video clip down to the minimum resolution specified. #[clap(short, long)] - transcode: Option<Resolution> + transcode: Option<Resolution>, + + /// Treat the audio as stereo. By default, only one channel from the input stereo will + /// be used, assuming either the other channel is backup or the same as the used. + #[clap(short, long, default_value = "false")] + stereo: bool } macro_rules! resolutions { @@ -133,6 +138,7 @@ struct ProjectLecture { #[derive(Deserialize, Serialize)] struct ProjectSource { files: Vec<String>, + stereo: bool, #[serde_as(as = "Option<DisplayFromStr>")] start: Option<Time>, @@ -171,6 +177,18 @@ struct ProjectProgress { transcoded: BTreeSet<Resolution> } +fn ask(question: impl Display) -> String { + let mut stdout = io::stdout().lock(); + let mut stdin = io::stdin().lock(); + + writeln!(stdout, "{question}").unwrap(); + let mut line = String::new(); + write!(stdout, "> ").unwrap(); + stdout.flush().unwrap(); + stdin.read_line(&mut line).unwrap(); + line.trim().to_owned() +} + fn ask_time(question: impl Display) -> Time { let mut stdout = io::stdout().lock(); let mut stdin = io::stdin().lock(); @@ -212,7 +230,9 @@ fn main() { let entry = entry.unwrap(); let name = entry.file_name(); let lower = name.to_ascii_lowercase(); - if (lower.ends_with(".mp4") || lower.ends_with(".mts")) + if (lower.ends_with(".mp4") + || lower.ends_with(".mts") + || lower.ends_with(".mkv")) && entry.file_type().unwrap().is_file() { files.push(String::from(name)); @@ -220,12 +240,19 @@ fn main() { } files.sort_unstable(); assert!(!files.is_empty()); + println!("I found the following source files: {files:?}"); + files = ask("Which source files would you like to use? (specify multiple files separated by whitespace)") + .split_ascii_whitespace() + .map(String::from) + .collect(); + assert!(!files.is_empty()); let project = Project { lecture: ProjectLecture { course, date }, source: ProjectSource { files, + stereo: args.stereo, start: None, end: None, fast: Vec::new(), diff --git a/src/render/ffmpeg.rs b/src/render/ffmpeg.rs index 543ad7d8e00b7913729ea685d448dc2222cb737c..00597caa250a11dbf9c734b7685db3d2a23999da 100644 --- a/src/render/ffmpeg.rs +++ b/src/render/ffmpeg.rs @@ -157,7 +157,9 @@ enum FfmpegFilter { filters: Vec<Filter>, output: Cow<'static, str> }, - Loudnorm, + Loudnorm { + stereo: bool + }, Rescale(Resolution) } @@ -216,10 +218,14 @@ impl Ffmpeg { self } - pub fn enable_loudnorm(&mut self) -> &mut Self { + pub fn enable_loudnorm(&mut self, loudnorm_stereo: bool) -> &mut Self { match &mut self.filter { - FfmpegFilter::None => self.filter = FfmpegFilter::Loudnorm, - FfmpegFilter::Loudnorm => {}, + FfmpegFilter::None => { + self.filter = FfmpegFilter::Loudnorm { + stereo: loudnorm_stereo + } + }, + FfmpegFilter::Loudnorm { stereo } if *stereo == loudnorm_stereo => {}, _ => panic!("An incompatible type of filter has been set before") } self @@ -228,7 +234,6 @@ impl Ffmpeg { pub fn rescale_video(&mut self, res: Resolution) -> &mut Self { match &mut self.filter { FfmpegFilter::None => self.filter = FfmpegFilter::Rescale(res), - FfmpegFilter::Loudnorm => {}, _ => panic!("An incompatible type of filter has been set before") } self @@ -243,7 +248,7 @@ impl Ffmpeg { let (vdec, venc, aenc) = match &self.filter { FfmpegFilter::None => (false, false, false), FfmpegFilter::Filters { .. } => (false, true, true), - FfmpegFilter::Loudnorm => (false, false, true), + FfmpegFilter::Loudnorm { .. } => (false, false, true), FfmpegFilter::Rescale(_) => (true, true, false) }; @@ -286,7 +291,7 @@ impl Ffmpeg { cmd.arg("-map").arg("[v]"); cmd.arg("-map").arg(channel('a', &output)); }, - FfmpegFilter::Loudnorm => { + FfmpegFilter::Loudnorm { stereo: false } => { cmd.arg("-af").arg(concat!( "pan=mono|c0=FR,", "loudnorm=dual_mono=true:print_format=summary,", @@ -294,6 +299,10 @@ impl Ffmpeg { "aformat=sample_rates=48000" )); }, + FfmpegFilter::Loudnorm { stereo: true } => { + cmd.arg("-af") + .arg("loudnorm=print_format=summary,aformat=sample_rates=48000"); + }, FfmpegFilter::Rescale(res) => { cmd.arg("-vf").arg(if vaapi { format!("scale_vaapi=w={}:h={}", res.width(), res.height()) diff --git a/src/render/mod.rs b/src/render/mod.rs index e2576e245815bec94b66e890d8e43f3687f6ced0..9bb4c3955d7b4fe1cac58deb3cb0dd78ff6855be 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -211,7 +211,7 @@ impl<'a> Renderer<'a> { concat: true, ..FfmpegInput::new(recording_txt) }); - ffmpeg.enable_loudnorm(); + ffmpeg.enable_loudnorm(project.source.stereo); ffmpeg.run()?; let width = ffprobe_video("stream=width", &recording_mkv)?.parse()?;