use AV1 for 1440p and higher
This commit is contained in:
parent
c288f55ed0
commit
f4adda912a
3 changed files with 131 additions and 84 deletions
26
src/main.rs
26
src/main.rs
|
@ -7,7 +7,7 @@ mod render;
|
||||||
mod time;
|
mod time;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render::Renderer,
|
render::{ffmpeg::FfmpegOutputFormat, Renderer},
|
||||||
time::{parse_date, parse_time, Date, Time}
|
time::{parse_date, parse_time, Date, Time}
|
||||||
};
|
};
|
||||||
use camino::Utf8PathBuf as PathBuf;
|
use camino::Utf8PathBuf as PathBuf;
|
||||||
|
@ -46,7 +46,7 @@ struct Args {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! resolutions {
|
macro_rules! resolutions {
|
||||||
($($res:ident: $width:literal x $height:literal at $bitrate:literal),+) => {
|
($($res:ident: $width:literal x $height:literal at $bitrate:literal in $format:ident),+) => {
|
||||||
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
enum Resolution {
|
enum Resolution {
|
||||||
|
@ -84,6 +84,12 @@ macro_rules! resolutions {
|
||||||
$(Self::$res => $bitrate),+
|
$(Self::$res => $bitrate),+
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format(self) -> FfmpegOutputFormat {
|
||||||
|
match self {
|
||||||
|
$(Self::$res => FfmpegOutputFormat::$format),+
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Resolution {
|
impl FromStr for Resolution {
|
||||||
|
@ -100,12 +106,12 @@ macro_rules! resolutions {
|
||||||
}
|
}
|
||||||
|
|
||||||
resolutions! {
|
resolutions! {
|
||||||
nHD: 640 x 360 at 500_000,
|
nHD: 640 x 360 at 500_000 in AvcAac,
|
||||||
HD: 1280 x 720 at 1_000_000,
|
HD: 1280 x 720 at 1_000_000 in AvcAac,
|
||||||
FullHD: 1920 x 1080 at 2_000_000,
|
FullHD: 1920 x 1080 at 2_000_000 in AvcAac,
|
||||||
WQHD: 2560 x 1440 at 3_000_000,
|
WQHD: 2560 x 1440 at 3_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
|
UHD: 3840 x 2160 at 4_000_000 in Av1Opus
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
@ -234,7 +240,7 @@ fn main() {
|
||||||
println!("{}", toml::to_string(&project).unwrap());
|
println!("{}", toml::to_string(&project).unwrap());
|
||||||
|
|
||||||
let renderer = Renderer::new(&directory, &project).unwrap();
|
let renderer = Renderer::new(&directory, &project).unwrap();
|
||||||
let recording = renderer.recording_mp4();
|
let recording = renderer.recording_mkv();
|
||||||
|
|
||||||
// preprocess the video
|
// preprocess the video
|
||||||
if !project.progress.preprocessed {
|
if !project.progress.preprocessed {
|
||||||
|
@ -282,7 +288,7 @@ fn main() {
|
||||||
// render the video
|
// render the video
|
||||||
let mut videos = Vec::new();
|
let mut videos = Vec::new();
|
||||||
videos.push(if project.progress.rendered {
|
videos.push(if project.progress.rendered {
|
||||||
renderer.video_mp4(&project)
|
renderer.video_file_output()
|
||||||
} else {
|
} else {
|
||||||
let video = renderer.render(&mut project).unwrap();
|
let video = renderer.render(&mut project).unwrap();
|
||||||
project.progress.rendered = true;
|
project.progress.rendered = true;
|
||||||
|
@ -302,7 +308,7 @@ fn main() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if !project.progress.transcoded.contains(&res) {
|
if !project.progress.transcoded.contains(&res) {
|
||||||
videos.push(renderer.rescale(res, &project).unwrap());
|
videos.push(renderer.rescale(res).unwrap());
|
||||||
project.progress.transcoded.insert(res);
|
project.progress.transcoded.insert(res);
|
||||||
|
|
||||||
println!("{}", toml::to_string(&project).unwrap());
|
println!("{}", toml::to_string(&project).unwrap());
|
||||||
|
|
|
@ -41,7 +41,9 @@ impl FfmpegInput {
|
||||||
cmd.arg("-r").arg(fps.to_string());
|
cmd.arg("-r").arg(fps.to_string());
|
||||||
}
|
}
|
||||||
if let Some(start) = self.start {
|
if let Some(start) = self.start {
|
||||||
cmd.arg("-seek_streams_individually").arg("false");
|
if self.path.ends_with(".mp4") {
|
||||||
|
cmd.arg("-seek_streams_individualy").arg("false");
|
||||||
|
}
|
||||||
cmd.arg("-ss").arg(format_time(start));
|
cmd.arg("-ss").arg(format_time(start));
|
||||||
}
|
}
|
||||||
if let Some(duration) = self.duration {
|
if let Some(duration) = self.duration {
|
||||||
|
@ -51,18 +53,35 @@ impl FfmpegInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) enum FfmpegOutputFormat {
|
||||||
|
/// AV1 / FLAC
|
||||||
|
Av1Flac,
|
||||||
|
/// AV1 / OPUS
|
||||||
|
Av1Opus,
|
||||||
|
/// AVC (H.264) / AAC
|
||||||
|
AvcAac
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct FfmpegOutput {
|
pub(crate) struct FfmpegOutput {
|
||||||
|
pub(crate) format: FfmpegOutputFormat,
|
||||||
|
pub(crate) audio_bitrate: Option<u64>,
|
||||||
|
pub(crate) video_bitrate: Option<u64>,
|
||||||
|
|
||||||
pub(crate) fps: Option<Rational>,
|
pub(crate) fps: Option<Rational>,
|
||||||
pub(crate) duration: Option<Time>,
|
pub(crate) duration: Option<Time>,
|
||||||
pub(crate) time_base: Option<Rational>,
|
pub(crate) time_base: Option<Rational>,
|
||||||
pub(crate) fps_mode_vfr: bool,
|
pub(crate) fps_mode_vfr: bool,
|
||||||
pub(crate) faststart: bool,
|
pub(crate) faststart: bool,
|
||||||
|
|
||||||
pub(crate) path: PathBuf
|
pub(crate) path: PathBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FfmpegOutput {
|
impl FfmpegOutput {
|
||||||
pub(crate) fn new(path: PathBuf) -> Self {
|
pub(crate) fn new(format: FfmpegOutputFormat, path: PathBuf) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
format,
|
||||||
|
audio_bitrate: None,
|
||||||
|
video_bitrate: None,
|
||||||
fps: None,
|
fps: None,
|
||||||
duration: None,
|
duration: None,
|
||||||
time_base: None,
|
time_base: None,
|
||||||
|
@ -77,7 +96,42 @@ impl FfmpegOutput {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_to_cmd(self, cmd: &mut Command) {
|
fn append_to_cmd(self, cmd: &mut Command, venc: bool, _aenc: bool, vaapi: bool) {
|
||||||
|
// select codec and bitrate
|
||||||
|
const QUALITY: &str = "22";
|
||||||
|
if venc {
|
||||||
|
let mut vcodec: String = match self.format {
|
||||||
|
FfmpegOutputFormat::Av1Flac | FfmpegOutputFormat::Av1Opus => "av1".into(),
|
||||||
|
FfmpegOutputFormat::AvcAac => "h264".into()
|
||||||
|
};
|
||||||
|
if vaapi {
|
||||||
|
vcodec = format!("{vcodec}_vaapi");
|
||||||
|
}
|
||||||
|
cmd.arg("-c:v").arg(vcodec);
|
||||||
|
|
||||||
|
if let Some(bv) = self.video_bitrate {
|
||||||
|
cmd.arg("-b:v").arg(bv.to_string());
|
||||||
|
} else if vaapi {
|
||||||
|
cmd.arg("-rc_mode").arg("CQP");
|
||||||
|
cmd.arg("-global_quality").arg(QUALITY);
|
||||||
|
} else {
|
||||||
|
cmd.arg("-crf").arg(QUALITY);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmd.arg("-c:v").arg("copy");
|
||||||
|
}
|
||||||
|
cmd.arg("-c:a").arg(match self.format {
|
||||||
|
FfmpegOutputFormat::Av1Flac => "flac",
|
||||||
|
FfmpegOutputFormat::Av1Opus => "libopus",
|
||||||
|
FfmpegOutputFormat::AvcAac => "aac"
|
||||||
|
});
|
||||||
|
if let Some(ba) = self.audio_bitrate {
|
||||||
|
cmd.arg("-b:a").arg(ba.to_string());
|
||||||
|
} else {
|
||||||
|
cmd.arg("-b:a").arg("128k");
|
||||||
|
}
|
||||||
|
|
||||||
|
// other output options
|
||||||
if let Some(fps) = self.fps {
|
if let Some(fps) = self.fps {
|
||||||
cmd.arg("-r").arg(fps.to_string());
|
cmd.arg("-r").arg(fps.to_string());
|
||||||
}
|
}
|
||||||
|
@ -110,7 +164,6 @@ enum FfmpegFilter {
|
||||||
pub(crate) struct Ffmpeg {
|
pub(crate) struct Ffmpeg {
|
||||||
inputs: Vec<FfmpegInput>,
|
inputs: Vec<FfmpegInput>,
|
||||||
filter: FfmpegFilter,
|
filter: FfmpegFilter,
|
||||||
video_bitrate: Option<u64>,
|
|
||||||
output: FfmpegOutput,
|
output: FfmpegOutput,
|
||||||
|
|
||||||
filter_idx: usize
|
filter_idx: usize
|
||||||
|
@ -121,7 +174,6 @@ impl Ffmpeg {
|
||||||
Self {
|
Self {
|
||||||
inputs: Vec::new(),
|
inputs: Vec::new(),
|
||||||
filter: FfmpegFilter::None,
|
filter: FfmpegFilter::None,
|
||||||
video_bitrate: None,
|
|
||||||
output,
|
output,
|
||||||
|
|
||||||
filter_idx: 0
|
filter_idx: 0
|
||||||
|
@ -182,11 +234,6 @@ impl Ffmpeg {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_video_bitrate(&mut self, bitrate: u64) -> &mut Self {
|
|
||||||
self.video_bitrate = Some(bitrate);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(mut self) -> anyhow::Result<()> {
|
pub fn run(mut self) -> anyhow::Result<()> {
|
||||||
let mut cmd = cmd();
|
let mut cmd = cmd();
|
||||||
cmd.arg("ffmpeg").arg("-hide_banner").arg("-y");
|
cmd.arg("ffmpeg").arg("-hide_banner").arg("-y");
|
||||||
|
@ -256,35 +303,7 @@ impl Ffmpeg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// append encoding options
|
self.output.append_to_cmd(&mut cmd, venc, aenc, vaapi);
|
||||||
const QUALITY: &str = "22";
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 aenc {
|
|
||||||
cmd.arg("-c:a").arg("aac");
|
|
||||||
cmd.arg("-b:a").arg("128000");
|
|
||||||
} else {
|
|
||||||
cmd.arg("-c:a").arg("copy");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.output.append_to_cmd(&mut cmd);
|
|
||||||
|
|
||||||
let status = cmd.status()?;
|
let status = cmd.status()?;
|
||||||
if status.success() {
|
if status.success() {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
pub mod ffmpeg;
|
pub mod ffmpeg;
|
||||||
mod filter;
|
mod filter;
|
||||||
|
|
||||||
use self::{ffmpeg::FfmpegOutput, filter::Filter};
|
use self::{
|
||||||
|
ffmpeg::{FfmpegOutput, FfmpegOutputFormat},
|
||||||
|
filter::Filter
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
iotro::{intro, outro},
|
iotro::{intro, outro},
|
||||||
render::ffmpeg::{Ffmpeg, FfmpegInput},
|
render::ffmpeg::{Ffmpeg, FfmpegInput},
|
||||||
|
@ -114,19 +117,17 @@ pub(crate) struct Renderer<'a> {
|
||||||
target: PathBuf
|
target: PathBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
fn svg2mp4(
|
fn svg2mkv(
|
||||||
meta: &ProjectSourceMetadata,
|
meta: &ProjectSourceMetadata,
|
||||||
svg: PathBuf,
|
svg: PathBuf,
|
||||||
mp4: PathBuf,
|
mkv: PathBuf,
|
||||||
duration: Time
|
duration: Time
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let mut ffmpeg = Ffmpeg::new(FfmpegOutput {
|
let mut ffmpeg = Ffmpeg::new(FfmpegOutput {
|
||||||
fps: None,
|
|
||||||
duration: Some(duration),
|
duration: Some(duration),
|
||||||
time_base: Some(meta.source_tbn),
|
time_base: Some(meta.source_tbn),
|
||||||
fps_mode_vfr: true,
|
fps_mode_vfr: true,
|
||||||
faststart: false,
|
..FfmpegOutput::new(FfmpegOutputFormat::Av1Flac, mkv)
|
||||||
path: mp4
|
|
||||||
});
|
});
|
||||||
ffmpeg.add_input(FfmpegInput {
|
ffmpeg.add_input(FfmpegInput {
|
||||||
loop_input: true,
|
loop_input: true,
|
||||||
|
@ -176,8 +177,16 @@ impl<'a> Renderer<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn recording_mp4(&self) -> PathBuf {
|
pub(crate) fn recording_mkv(&self) -> PathBuf {
|
||||||
self.target.join("recording.mp4")
|
self.target.join("recording.mkv")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intro_mkv(&self) -> PathBuf {
|
||||||
|
self.target.join("intro.mkv")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outro_mkv(&self) -> PathBuf {
|
||||||
|
self.target.join("outro.mkv")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn preprocess(&self, project: &mut Project) -> anyhow::Result<()> {
|
pub(crate) fn preprocess(&self, project: &mut Project) -> anyhow::Result<()> {
|
||||||
|
@ -193,8 +202,11 @@ impl<'a> Renderer<'a> {
|
||||||
println!("\x1B[1m ==> Concatenating Video and Normalising Audio ...\x1B[0m");
|
println!("\x1B[1m ==> Concatenating Video and Normalising Audio ...\x1B[0m");
|
||||||
let source_sample_rate =
|
let source_sample_rate =
|
||||||
ffprobe_audio("stream=sample_rate", &recording_txt)?.parse()?;
|
ffprobe_audio("stream=sample_rate", &recording_txt)?.parse()?;
|
||||||
let recording_mp4 = self.recording_mp4();
|
let recording_mkv = self.recording_mkv();
|
||||||
let mut ffmpeg = Ffmpeg::new(FfmpegOutput::new(recording_mp4.clone()));
|
let mut ffmpeg = Ffmpeg::new(FfmpegOutput::new(
|
||||||
|
FfmpegOutputFormat::Av1Flac,
|
||||||
|
recording_mkv.clone()
|
||||||
|
));
|
||||||
ffmpeg.add_input(FfmpegInput {
|
ffmpeg.add_input(FfmpegInput {
|
||||||
concat: true,
|
concat: true,
|
||||||
..FfmpegInput::new(recording_txt)
|
..FfmpegInput::new(recording_txt)
|
||||||
|
@ -202,8 +214,8 @@ impl<'a> Renderer<'a> {
|
||||||
ffmpeg.enable_loudnorm();
|
ffmpeg.enable_loudnorm();
|
||||||
ffmpeg.run()?;
|
ffmpeg.run()?;
|
||||||
|
|
||||||
let width = ffprobe_video("stream=width", &recording_mp4)?.parse()?;
|
let width = ffprobe_video("stream=width", &recording_mkv)?.parse()?;
|
||||||
let height = ffprobe_video("stream=height", &recording_mp4)?.parse()?;
|
let height = ffprobe_video("stream=height", &recording_mkv)?.parse()?;
|
||||||
let source_res = match (width, height) {
|
let source_res = match (width, height) {
|
||||||
(3840, 2160) => Resolution::UHD,
|
(3840, 2160) => Resolution::UHD,
|
||||||
(2560, 1440) => Resolution::WQHD,
|
(2560, 1440) => Resolution::WQHD,
|
||||||
|
@ -213,9 +225,9 @@ impl<'a> Renderer<'a> {
|
||||||
(width, height) => bail!("Unknown resolution: {width}x{height}")
|
(width, height) => bail!("Unknown resolution: {width}x{height}")
|
||||||
};
|
};
|
||||||
project.source.metadata = Some(ProjectSourceMetadata {
|
project.source.metadata = Some(ProjectSourceMetadata {
|
||||||
source_duration: ffprobe_video("format=duration", &recording_mp4)?.parse()?,
|
source_duration: ffprobe_video("format=duration", &recording_mkv)?.parse()?,
|
||||||
source_fps: ffprobe_video("stream=r_frame_rate", &recording_mp4)?.parse()?,
|
source_fps: ffprobe_video("stream=r_frame_rate", &recording_mkv)?.parse()?,
|
||||||
source_tbn: ffprobe_video("stream=time_base", &recording_mp4)?.parse()?,
|
source_tbn: ffprobe_video("stream=time_base", &recording_mkv)?.parse()?,
|
||||||
source_res,
|
source_res,
|
||||||
source_sample_rate
|
source_sample_rate
|
||||||
});
|
});
|
||||||
|
@ -231,8 +243,8 @@ impl<'a> Renderer<'a> {
|
||||||
.to_string_pretty()
|
.to_string_pretty()
|
||||||
.into_bytes()
|
.into_bytes()
|
||||||
)?;
|
)?;
|
||||||
let intro_mp4 = self.target.join("intro.mp4");
|
let intro_mkv = self.intro_mkv();
|
||||||
svg2mp4(metadata, intro_svg, intro_mp4, INTRO_LEN)?;
|
svg2mkv(metadata, intro_svg, intro_mkv, INTRO_LEN)?;
|
||||||
|
|
||||||
// render outro to svg then mp4
|
// render outro to svg then mp4
|
||||||
let outro_svg = self.target.join("outro.svg");
|
let outro_svg = self.target.join("outro.svg");
|
||||||
|
@ -240,8 +252,8 @@ impl<'a> Renderer<'a> {
|
||||||
&outro_svg,
|
&outro_svg,
|
||||||
outro(source_res).to_string_pretty().into_bytes()
|
outro(source_res).to_string_pretty().into_bytes()
|
||||||
)?;
|
)?;
|
||||||
let outro_mp4 = self.target.join("outro.mp4");
|
let outro_mkv = self.outro_mkv();
|
||||||
svg2mp4(metadata, outro_svg, outro_mp4, OUTRO_LEN)?;
|
svg2mkv(metadata, outro_svg, outro_mkv, OUTRO_LEN)?;
|
||||||
|
|
||||||
// copy logo then render to png
|
// copy logo then render to png
|
||||||
let logo_svg = self.target.join("logo.svg");
|
let logo_svg = self.target.join("logo.svg");
|
||||||
|
@ -271,25 +283,35 @@ impl<'a> Renderer<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn video_mp4_res(&self, res: Resolution) -> PathBuf {
|
/// Get the video file for a specific resolution, completely finished.
|
||||||
|
fn video_file_res(&self, res: Resolution) -> PathBuf {
|
||||||
|
let extension = match res.format() {
|
||||||
|
FfmpegOutputFormat::Av1Flac => "mkv",
|
||||||
|
FfmpegOutputFormat::Av1Opus => "webm",
|
||||||
|
FfmpegOutputFormat::AvcAac => "mp4"
|
||||||
|
};
|
||||||
self.target
|
self.target
|
||||||
.join(format!("{}-{}p.mp4", self.slug, res.height()))
|
.join(format!("{}-{}p.{extension}", self.slug, res.height()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn video_mp4(&self, project: &Project) -> PathBuf {
|
/// Get the video file directly outputed to further transcode.
|
||||||
self.video_mp4_res(project.source.metadata.as_ref().unwrap().source_res)
|
pub(crate) fn video_file_output(&self) -> PathBuf {
|
||||||
|
self.target.join(format!("{}.mkv", self.slug))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn render(&self, project: &mut Project) -> anyhow::Result<PathBuf> {
|
pub(crate) fn render(&self, project: &mut Project) -> anyhow::Result<PathBuf> {
|
||||||
let source_res = project.source.metadata.as_ref().unwrap().source_res;
|
let source_res = project.source.metadata.as_ref().unwrap().source_res;
|
||||||
|
|
||||||
let output = self.video_mp4(project);
|
let output = self.video_file_output();
|
||||||
let mut ffmpeg = Ffmpeg::new(FfmpegOutput::new(output.clone()));
|
let mut ffmpeg = Ffmpeg::new(FfmpegOutput {
|
||||||
|
video_bitrate: Some(source_res.bitrate() * 3),
|
||||||
|
..FfmpegOutput::new(FfmpegOutputFormat::Av1Flac, output.clone())
|
||||||
|
});
|
||||||
|
|
||||||
// add all of our inputs
|
// add all of our inputs
|
||||||
let intro = ffmpeg.add_input(FfmpegInput::new(self.target.join("intro.mp4")));
|
let intro = ffmpeg.add_input(FfmpegInput::new(self.intro_mkv()));
|
||||||
let rec_file = self.target.join("recording.mp4");
|
let rec_file = self.recording_mkv();
|
||||||
let outro = ffmpeg.add_input(FfmpegInput::new(self.target.join("outro.mp4")));
|
let outro = ffmpeg.add_input(FfmpegInput::new(self.outro_mkv()));
|
||||||
let logo = ffmpeg.add_input(FfmpegInput::new(self.target.join("logo.png")));
|
let logo = ffmpeg.add_input(FfmpegInput::new(self.target.join("logo.png")));
|
||||||
let ff = ffmpeg.add_input(FfmpegInput::new(self.target.join("fastforward.png")));
|
let ff = ffmpeg.add_input(FfmpegInput::new(self.target.join("fastforward.png")));
|
||||||
|
|
||||||
|
@ -444,22 +466,22 @@ impl<'a> Renderer<'a> {
|
||||||
|
|
||||||
// we're done :)
|
// we're done :)
|
||||||
ffmpeg.set_filter_output(overlay);
|
ffmpeg.set_filter_output(overlay);
|
||||||
ffmpeg.set_video_bitrate(source_res.bitrate() * 3);
|
|
||||||
ffmpeg.run()?;
|
ffmpeg.run()?;
|
||||||
|
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rescale(&self, res: Resolution, project: &Project) -> anyhow::Result<PathBuf> {
|
pub fn rescale(&self, res: Resolution) -> anyhow::Result<PathBuf> {
|
||||||
let input = self.video_mp4(project);
|
let input = self.video_file_output();
|
||||||
let output = self.video_mp4_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 =
|
let mut ffmpeg = Ffmpeg::new(FfmpegOutput {
|
||||||
Ffmpeg::new(FfmpegOutput::new(output.clone()).enable_faststart());
|
video_bitrate: Some(res.bitrate()),
|
||||||
|
..FfmpegOutput::new(res.format(), output.clone()).enable_faststart()
|
||||||
|
});
|
||||||
ffmpeg.add_input(FfmpegInput::new(input));
|
ffmpeg.add_input(FfmpegInput::new(input));
|
||||||
ffmpeg.rescale_video(res);
|
ffmpeg.rescale_video(res);
|
||||||
ffmpeg.set_video_bitrate(res.bitrate());
|
|
||||||
ffmpeg.run()?;
|
ffmpeg.run()?;
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue