more filter logic

This commit is contained in:
Dominic 2023-10-30 16:05:21 +01:00
parent fd5d3ab1fd
commit a0f1caa34b
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
4 changed files with 216 additions and 60 deletions

View file

@ -1,3 +1,4 @@
#![allow(clippy::manual_range_contains)]
#![warn(rust_2018_idioms)]
#![forbid(elided_lifetimes_in_paths, unsafe_code)]
@ -27,7 +28,7 @@ struct Args {
course: String
}
#[allow(non_camel_case_types)]
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[derive(Clone, Copy, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
enum Resolution {
/// 640x360

View file

@ -1,4 +1,4 @@
use super::cmd;
use super::{cmd, filter::Filter};
use crate::time::{format_time, Time};
use camino::{Utf8Path as Path, Utf8PathBuf as PathBuf};
use rational::Rational;
@ -45,66 +45,12 @@ impl FfmpegInput {
}
}
pub(crate) enum Filter {
Concat {
inputs: Vec<Cow<'static, str>>,
n: usize,
output: Cow<'static, str>
},
FadeIn {
input: Cow<'static, str>,
start: Time,
duration: Time,
output: Cow<'static, str>
},
FadeOut {
input: Cow<'static, str>,
start: Time,
duration: Time,
output: Cow<'static, str>
},
Overlay {
video_input: Cow<'static, str>,
overlay_input: Cow<'static, str>,
x: Cow<'static, str>,
y: Cow<'static, str>,
output: Cow<'static, str>
},
GenerateSilence {
output: Cow<'static, str>
}
}
impl Filter {
fn is_video_filter(&self) -> bool {
matches!(
self,
Self::Concat { .. }
| Self::FadeIn { .. }
| Self::FadeOut { .. }
| Self::Overlay { .. }
)
}
fn is_audio_filter(&self) -> bool {
matches!(
self,
Self::Concat { .. }
| Self::FadeIn { .. }
| Self::FadeOut { .. }
| Self::GenerateSilence { .. }
)
}
}
pub(crate) struct Ffmpeg {
inputs: Vec<FfmpegInput>,
filters: Vec<Filter>,
output: PathBuf
output: PathBuf,
filter_idx: usize
}
impl Ffmpeg {
@ -112,7 +58,9 @@ impl Ffmpeg {
Self {
inputs: Vec::new(),
filters: Vec::new(),
output
output,
filter_idx: 0
}
}
@ -152,6 +100,12 @@ impl Ffmpeg {
} 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");
}
unimplemented!()
}

200
src/render/filter.rs Normal file
View file

@ -0,0 +1,200 @@
use crate::time::{format_time, Time};
use std::{borrow::Cow, fmt::Write as _};
pub(crate) enum Filter {
/// Trim audio and video alike
Trim {
input: Cow<'static, str>,
start: Option<Time>,
duration: Option<Time>,
output: Cow<'static, str>
},
/// Apply an alpha channel on the video. No audio.
Alpha {
input: Cow<'static, str>,
alpha: f32,
output: Cow<'static, str>
},
/// Overlay the one video over the other. The audio is copied.
Overlay {
video_input: Cow<'static, str>,
overlay_input: Cow<'static, str>,
x: Cow<'static, str>,
y: Cow<'static, str>,
output: Cow<'static, str>
},
/// Concatenate audio and video.
Concat {
inputs: Vec<Cow<'static, str>>,
n: usize,
output: Cow<'static, str>
},
/// Fade audio and video.
Fade {
input: Cow<'static, str>,
direction: &'static str,
start: Time,
duration: Time,
output: Cow<'static, str>
},
/// Generate silence. The video is copied.
GenerateSilence {
video: Cow<'static, str>,
output: Cow<'static, str>
}
}
impl Filter {
pub(crate) fn is_video_filter(&self) -> bool {
matches!(
self,
Self::Trim { .. }
| Self::Alpha { .. }
| Self::Concat { .. }
| Self::Fade { .. }
| Self::Overlay { .. }
)
}
pub(crate) fn is_audio_filter(&self) -> bool {
matches!(
self,
Self::Trim { .. }
| Self::Concat { .. }
| Self::Fade { .. }
| Self::GenerateSilence { .. }
)
}
fn append_to_complex_filter(&self, complex: &mut String, filter_idx: &mut usize) {
match self {
Self::Trim {
input,
start,
duration,
output
} => {
let mut args = String::new();
if let Some(start) = start {
write!(args, "start={}", format_time(*start));
}
if let Some(duration) = duration {
if !args.is_empty() {
args += ":";
}
write!(args, "duration={}", format_time(*duration));
}
writeln!(
complex,
"{}trim={args},setpts=PTS-STARTPTS{};",
channel('v', input),
channel('v', output)
);
writeln!(
complex,
"{}atrim={args},asetpts=PTS-STARTPTS{};",
channel('a', input),
channel('a', output)
);
},
Self::Alpha {
input,
alpha,
output
} => {
writeln!(
complex,
"{}format=yuva444p,colorchannelmixer=aa={alpha}{};",
channel('v', input),
channel('v', output)
);
},
Self::Overlay {
video_input,
overlay_input,
x,
y,
output
} => {
writeln!(
complex,
"{}{}overlay=x={x}:y={y}{};",
channel('v', video_input),
channel('v', overlay_input),
channel('v', output)
);
writeln!(
complex,
"{}anull{};",
channel('a', video_input),
channel('a', output)
);
},
Self::Concat { inputs, n, output } => {
for i in inputs {
write!(complex, "{}{}", channel('v', i), channel('a', i));
}
writeln!(
complex,
"concat=n={n}:v=1:a=1{}{};",
channel('v', output),
channel('a', output)
);
},
Self::Fade {
input,
direction,
start,
duration,
output
} => {
let args = format!(
"{direction}:st={}:d={}",
format_time(*start),
format_time(*duration)
);
writeln!(
complex,
"{}fade={args}{};",
channel('v', input),
channel('v', output)
);
writeln!(
complex,
"{}afade=t={args}{};",
channel('a', input),
channel('a', output)
);
},
Self::GenerateSilence { video, output } => {
writeln!(
complex,
"{}null{};",
channel('v', video),
channel('v', output)
);
writeln!(complex, "aevalsrc=0:s=48000{};", channel('a', output));
},
_ => unimplemented!()
}
}
}
fn channel(channel: char, id: &str) -> String {
if id.chars().any(|ch| !ch.is_digit(10)) {
format!("[{channel}_{id}]")
} else {
format!("[{id}:{channel}]")
}
}

View file

@ -1,6 +1,7 @@
#![allow(warnings)]
pub mod ffmpeg;
mod filter;
use crate::{
iotro::intro,