Compare commits

...

2 commits

Author SHA1 Message Date
98f415ade7
add metadata to transcoded videos 2024-05-24 12:17:40 +02:00
9a4b3142ff
optimize some libsvtav1 params 2024-05-24 12:01:45 +02:00
4 changed files with 81 additions and 20 deletions

View file

@ -13,16 +13,18 @@ use svgwriter::{
#[derive(Clone)] #[derive(Clone)]
pub struct Language<'a> { pub struct Language<'a> {
lang: &'a str, pub(crate) lang: &'a str,
format_date_long: fn(Date) -> String, pub(crate) format_date_long: fn(Date) -> String,
// intro // intro
lecture_from: &'a str, lecture_from: &'a str,
video_created_by_us: &'a str, pub(crate) video_created_by_us: &'a str,
// outro // outro
video_created_by: &'a str, video_created_by: &'a str,
our_website: &'a str, our_website: &'a str,
download_videos: &'a str, download_videos: &'a str,
questions_feedback: &'a str questions_feedback: &'a str,
// metadata
pub(crate) from: &'a str
} }
pub const GERMAN: Language<'static> = Language { pub const GERMAN: Language<'static> = Language {
@ -54,7 +56,9 @@ pub const GERMAN: Language<'static> = Language {
video_created_by: "Video erstellt von der", video_created_by: "Video erstellt von der",
our_website: "Website der Fachschaft", our_website: "Website der Fachschaft",
download_videos: "Videos herunterladen", download_videos: "Videos herunterladen",
questions_feedback: "Fragen, Vorschläge und Feedback" questions_feedback: "Fragen, Vorschläge und Feedback",
from: "vom"
}; };
pub const BRITISH: Language<'static> = Language { pub const BRITISH: Language<'static> = Language {
@ -88,10 +92,13 @@ pub const BRITISH: Language<'static> = Language {
lecture_from: "Lecture from", lecture_from: "Lecture from",
video_created_by_us: "Video created by the Video AG, Fachschaft I/1", video_created_by_us: "Video created by the Video AG, Fachschaft I/1",
video_created_by: "Video created by the", video_created_by: "Video created by the",
our_website: "The Fachschaft's website", our_website: "The Fachschaft's website",
download_videos: "Download videos", download_videos: "Download videos",
questions_feedback: "Questions, Suggestions and Feedback" questions_feedback: "Questions, Suggestions and Feedback",
from: "from"
}; };
impl Default for Language<'static> { impl Default for Language<'static> {

View file

@ -133,10 +133,10 @@ macro_rules! resolutions {
resolutions! { resolutions! {
nHD: 640 x 360 at 500_000 in AvcAac, nHD: 640 x 360 at 500_000 in AvcAac,
HD: 1280 x 720 at 1_000_000 in AvcAac, HD: 1280 x 720 at 1_000_000 in AvcAac,
FullHD: 1920 x 1080 at 2_000_000 in Av1Opus, FullHD: 1920 x 1080 at 750_000 in Av1Opus,
WQHD: 2560 x 1440 at 3_000_000 in Av1Opus, WQHD: 2560 x 1440 at 1_000_000 in Av1Opus,
// TODO qsx muss mal sagen wieviel bitrate für 4k // TODO qsx muss mal sagen wieviel bitrate für 4k
UHD: 3840 x 2160 at 4_000_000 in Av1Opus UHD: 3840 x 2160 at 2_000_000 in Av1Opus
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
@ -369,7 +369,7 @@ fn main() {
continue; continue;
} }
if !project.progress.transcoded.contains(&res) { if !project.progress.transcoded.contains(&res) {
videos.push(renderer.rescale(res).unwrap()); videos.push(renderer.rescale(&project.lecture, res).unwrap());
project.progress.transcoded.insert(res); project.progress.transcoded.insert(res);
fs::write(&project_path, toml::to_string(&project).unwrap().as_bytes()) fs::write(&project_path, toml::to_string(&project).unwrap().as_bytes())

View file

@ -74,6 +74,14 @@ pub(crate) struct FfmpegOutput {
pub(crate) fps_mode_vfr: bool, pub(crate) fps_mode_vfr: bool,
pub(crate) faststart: bool, pub(crate) faststart: bool,
// video container metadata
pub(crate) title: Option<String>,
pub(crate) author: Option<String>,
pub(crate) album: Option<String>,
pub(crate) year: Option<String>,
pub(crate) comment: Option<String>,
pub(crate) language: Option<String>,
pub(crate) path: PathBuf pub(crate) path: PathBuf
} }
@ -83,11 +91,20 @@ impl FfmpegOutput {
format, format,
audio_bitrate: None, audio_bitrate: None,
video_bitrate: None, video_bitrate: None,
fps: None, fps: None,
duration: None, duration: None,
time_base: None, time_base: None,
fps_mode_vfr: false, fps_mode_vfr: false,
faststart: false, faststart: false,
title: None,
author: None,
album: None,
year: None,
comment: None,
language: None,
path path
} }
} }
@ -102,7 +119,7 @@ impl FfmpegOutput {
fn append_to_cmd(self, cmd: &mut Command, venc: bool, _aenc: bool, vaapi: bool) { fn append_to_cmd(self, cmd: &mut Command, venc: bool, _aenc: bool, vaapi: bool) {
// select codec and bitrate // select codec and bitrate
const QUALITY: &str = "22"; const QUALITY: &str = "18";
if venc { if venc {
let vcodec = match (self.format, vaapi) { let vcodec = match (self.format, vaapi) {
(FfmpegOutputFormat::Av1Flac, false) (FfmpegOutputFormat::Av1Flac, false)
@ -114,13 +131,22 @@ impl FfmpegOutput {
}; };
cmd.arg("-c:v").arg(vcodec); cmd.arg("-c:v").arg(vcodec);
if let Some(bv) = self.video_bitrate { if vcodec == "libsvtav1" {
cmd.arg("-b:v").arg(bv.to_string()); cmd.arg("-svtav1-params").arg("fast-decode=1");
} else if vaapi { cmd.arg("-preset").arg("8");
cmd.arg("-rc_mode").arg("CQP"); }
cmd.arg("-global_quality").arg(QUALITY);
} else { match self.video_bitrate {
cmd.arg("-crf").arg(QUALITY); Some(bv) if vcodec != "libsvtav1" => {
cmd.arg("-b:v").arg(bv.to_string());
},
None if vaapi => {
cmd.arg("-rc_mode").arg("CQP");
cmd.arg("-global_quality").arg(QUALITY);
},
_ => {
cmd.arg("-crf").arg(QUALITY);
}
} }
} else { } else {
cmd.arg("-c:v").arg("copy"); cmd.arg("-c:v").arg("copy");
@ -152,6 +178,17 @@ impl FfmpegOutput {
if self.faststart { if self.faststart {
cmd.arg("-movflags").arg("+faststart"); cmd.arg("-movflags").arg("+faststart");
} }
// metadata
macro_rules! add_meta {
($this:ident, $cmd:ident: $($meta:ident),+) => {
$(if let Some(value) = $this.$meta.as_deref() {
$cmd.arg("-metadata").arg(format!("{}={}", stringify!($meta), value));
})+
}
}
add_meta!(self, cmd: title, author, album, year, comment, language);
cmd.arg(self.path); cmd.arg(self.path);
} }
} }

View file

@ -9,7 +9,7 @@ use crate::{
iotro::{intro, outro}, iotro::{intro, outro},
render::ffmpeg::{Ffmpeg, FfmpegInput}, render::ffmpeg::{Ffmpeg, FfmpegInput},
time::{format_date, Time}, time::{format_date, Time},
Project, ProjectSourceMetadata, Resolution Project, ProjectLecture, ProjectSourceMetadata, Resolution
}; };
use anyhow::{bail, Context}; use anyhow::{bail, Context};
use camino::{Utf8Path as Path, Utf8PathBuf as PathBuf}; use camino::{Utf8Path as Path, Utf8PathBuf as PathBuf};
@ -501,13 +501,30 @@ impl<'a> Renderer<'a> {
Ok(output) Ok(output)
} }
pub fn rescale(&self, res: Resolution) -> anyhow::Result<PathBuf> { pub fn rescale(
&self,
lecture: &ProjectLecture,
res: Resolution
) -> anyhow::Result<PathBuf> {
let input = self.video_file_output(); let input = self.video_file_output();
let output = self.video_file_res(res); let output = self.video_file_res(res);
println!("\x1B[1m ==> Rescaling to {}p\x1B[0m", res.height()); println!("\x1B[1m ==> Rescaling to {}p\x1B[0m", res.height());
let mut ffmpeg = Ffmpeg::new(FfmpegOutput { let mut ffmpeg = Ffmpeg::new(FfmpegOutput {
video_bitrate: Some(res.bitrate()), video_bitrate: Some(res.bitrate()),
title: Some(format!(
"{} {} {}",
lecture.label,
lecture.lang.from,
(lecture.lang.format_date_long)(lecture.date)
)),
author: Some(lecture.docent.clone()),
album: Some(lecture.course.clone()),
year: Some(lecture.date.year.to_string()),
comment: Some(lecture.lang.video_created_by_us.into()),
language: Some(lecture.lang.lang.into()),
..FfmpegOutput::new(res.format(), output.clone()).enable_faststart() ..FfmpegOutput::new(res.format(), output.clone()).enable_faststart()
}); });
ffmpeg.add_input(FfmpegInput::new(input)); ffmpeg.add_input(FfmpegInput::new(input));