support rescaling
This commit is contained in:
parent
268c4b3af7
commit
feb8596bfc
2 changed files with 125 additions and 46 deletions
99
src/main.rs
99
src/main.rs
|
@ -37,41 +37,56 @@ struct Args {
|
||||||
mem_limit: String
|
mem_limit: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
macro_rules! resolutions {
|
||||||
#[derive(Clone, Copy, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
($($res:ident: $width:literal x $height:literal at $bitrate:literal),+) => {
|
||||||
enum Resolution {
|
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
/// 640x360
|
#[derive(Clone, Copy, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
nHD,
|
enum Resolution {
|
||||||
/// 1280x720
|
$(
|
||||||
HD,
|
#[doc = concat!(stringify!($width), "x", stringify!($height))]
|
||||||
/// 1920x1080
|
$res
|
||||||
FullHD,
|
),+
|
||||||
/// 2560x1440
|
}
|
||||||
WQHD,
|
|
||||||
/// 3840x2160
|
const NUM_RESOLUTIONS: usize = {
|
||||||
UHD
|
let mut num = 0;
|
||||||
}
|
$(num += 1; stringify!($res);)+
|
||||||
|
num
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Resolution {
|
||||||
|
fn values() -> [Self; NUM_RESOLUTIONS] {
|
||||||
|
[$(Self::$res),+]
|
||||||
|
}
|
||||||
|
|
||||||
impl Resolution {
|
|
||||||
fn width(self) -> usize {
|
fn width(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::nHD => 640,
|
$(Self::$res => $width),+
|
||||||
Self::HD => 1280,
|
|
||||||
Self::FullHD => 1920,
|
|
||||||
Self::WQHD => 2560,
|
|
||||||
Self::UHD => 3840
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn height(self) -> usize {
|
fn height(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::nHD => 360,
|
$(Self::$res => $height),+
|
||||||
Self::HD => 720,
|
|
||||||
Self::FullHD => 1080,
|
|
||||||
Self::WQHD => 1440,
|
|
||||||
Self::UHD => 2160
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bitrate(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
$(Self::$res => $bitrate),+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
@ -245,6 +260,38 @@ fn main() {
|
||||||
fs::write(&project_path, toml::to_string(&project).unwrap().as_bytes()).unwrap();
|
fs::write(&project_path, toml::to_string(&project).unwrap().as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render the video
|
||||||
|
let mut videos = Vec::new();
|
||||||
|
videos.push(if !project.progress.rendered {
|
||||||
let video = renderer.render(&mut project).unwrap();
|
let video = renderer.render(&mut project).unwrap();
|
||||||
println!("\x1B[1m ==> DONE :)\x1B[0m Video: {video}");
|
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}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,18 +261,17 @@ impl<'a> Renderer<'a> {
|
||||||
Ok(())
|
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<PathBuf> {
|
pub(crate) fn render(&self, project: &mut Project) -> anyhow::Result<PathBuf> {
|
||||||
let mut output = self.target.join(format!(
|
let output = self.video_mp4(project);
|
||||||
"{}-{}p.mp4",
|
|
||||||
self.slug,
|
|
||||||
project
|
|
||||||
.source
|
|
||||||
.metadata
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.source_res
|
|
||||||
.height()
|
|
||||||
));
|
|
||||||
let mut ffmpeg = Ffmpeg::new(output.clone());
|
let mut ffmpeg = Ffmpeg::new(output.clone());
|
||||||
|
|
||||||
// add all of our inputs
|
// add all of our inputs
|
||||||
|
@ -435,4 +434,37 @@ impl<'a> Renderer<'a> {
|
||||||
|
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rescale(&self, res: Resolution, project: &Project) -> anyhow::Result<PathBuf> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue