diff --git a/src/main.rs b/src/main.rs index 53df6ea..a0b32e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -122,7 +122,7 @@ fn main() { project }; - let renderer = Renderer::new(&directory, &project).unwrap(); + let mut renderer = Renderer::new(&directory, &project).unwrap(); let recording = renderer.recording_mkv(); // preprocess the video diff --git a/src/render/ffmpeg.rs b/src/render/ffmpeg.rs index 07bca84..8bcc37b 100644 --- a/src/render/ffmpeg.rs +++ b/src/render/ffmpeg.rs @@ -59,10 +59,22 @@ pub(crate) enum FfmpegOutputFormat { Av1Flac, /// AV1 / OPUS Av1Opus, + /// AVC (H.264) / FLAC + AvcFlac, /// AVC (H.264) / AAC AvcAac } +impl FfmpegOutputFormat { + pub(crate) fn with_flac_audio(self) -> Self { + match self { + Self::Av1Flac | Self::AvcFlac => self, + Self::Av1Opus => Self::Av1Flac, + Self::AvcAac => Self::AvcFlac + } + } +} + pub(crate) enum FfmpegOutputQuality { Default, VisuallyLossless @@ -132,8 +144,10 @@ impl FfmpegOutput { | (FfmpegOutputFormat::Av1Opus, false) => "libsvtav1", (FfmpegOutputFormat::Av1Flac, true) | (FfmpegOutputFormat::Av1Opus, true) => "av1_vaapi", - (FfmpegOutputFormat::AvcAac, false) => "h264", - (FfmpegOutputFormat::AvcAac, true) => "h264_vaapi" + (FfmpegOutputFormat::AvcAac, false) + | (FfmpegOutputFormat::AvcFlac, false) => "h264", + (FfmpegOutputFormat::AvcAac, true) + | (FfmpegOutputFormat::AvcFlac, true) => "h264_vaapi" }; cmd.arg("-c:v").arg(vcodec); @@ -164,7 +178,7 @@ impl FfmpegOutput { cmd.arg("-c:v").arg("copy"); } cmd.arg("-c:a").arg(match self.format { - FfmpegOutputFormat::Av1Flac => "flac", + FfmpegOutputFormat::Av1Flac | FfmpegOutputFormat::AvcFlac => "flac", FfmpegOutputFormat::Av1Opus => "libopus", FfmpegOutputFormat::AvcAac => "aac" }); diff --git a/src/render/mod.rs b/src/render/mod.rs index 332bea1..d426cf1 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -140,6 +140,7 @@ fn svg2mkv( duration: Time ) -> anyhow::Result<()> { let mut ffmpeg = Ffmpeg::new(FfmpegOutput { + quality: FfmpegOutputQuality::VisuallyLossless, duration: Some(duration), time_base: Some(meta.source_tbn), fps_mode_vfr: true, @@ -185,23 +186,21 @@ impl<'a> Renderer<'a> { let target = directory.join(&slug); fs::create_dir_all(&target)?; - let first: PathBuf = directory.join( - project - .source - .files - .first() - .context("No source files present")? - ); - let height: u32 = ffprobe_video("stream=height", &first)? - .split('\n') - .next() - .unwrap() - .parse()?; - let format = if height < 1080 { - FfmpegOutputFormat::AvcAac - } else { - FfmpegOutputFormat::Av1Flac - }; + // Ensure we have at least one input file. + project + .source + .files + .first() + .context("No source files present")?; + + // In case we don't have a resolution yet, we'll asign this after preprocessing. + let format = project + .source + .metadata + .as_ref() + .map(|meta| meta.source_res.default_codec()) + .unwrap_or(FfmpegOutputFormat::Av1Flac) + .with_flac_audio(); Ok(Self { directory, @@ -231,7 +230,7 @@ impl<'a> Renderer<'a> { self.target.join(format!("question{q_idx}.png")) } - pub fn preprocess(&self, project: &mut Project) -> anyhow::Result<()> { + pub fn preprocess(&mut self, project: &mut Project) -> anyhow::Result<()> { assert!(!project.progress.preprocessed); let recording_txt = self.target.join("recording.txt"); @@ -272,6 +271,7 @@ impl<'a> Renderer<'a> { source_res, source_sample_rate }); + self.format = source_res.default_codec().with_flac_audio(); Ok(()) } @@ -360,7 +360,7 @@ impl<'a> Renderer<'a> { /// Get the video file for a specific resolution, completely finished. fn video_file_res(&self, res: Resolution) -> PathBuf { let extension = match res.default_codec() { - FfmpegOutputFormat::Av1Flac => "mkv", + FfmpegOutputFormat::Av1Flac | FfmpegOutputFormat::AvcFlac => "mkv", FfmpegOutputFormat::Av1Opus => "webm", FfmpegOutputFormat::AvcAac => "mp4" };