62 lines
1.7 KiB
Rust
62 lines
1.7 KiB
Rust
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|||
|
|
pub enum VideoPipeline {
|
||
|
|
Copy,
|
||
|
|
Encode(VideoEncoder),
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
|
|
pub enum VideoEncoder {
|
||
|
|
Libx264,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl VideoPipeline {
|
||
|
|
pub fn ffmpeg_args(self, segment_seconds: u8) -> Vec<String> {
|
||
|
|
match self {
|
||
|
|
Self::Copy => vec!["-c:v".into(), "copy".into()],
|
||
|
|
Self::Encode(encoder) => encoder.ffmpeg_args(segment_seconds),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl VideoEncoder {
|
||
|
|
pub fn portable_software() -> Self {
|
||
|
|
Self::Libx264
|
||
|
|
}
|
||
|
|
|
||
|
|
fn ffmpeg_args(self, segment_seconds: u8) -> Vec<String> {
|
||
|
|
match self {
|
||
|
|
Self::Libx264 => vec![
|
||
|
|
"-c:v".into(),
|
||
|
|
"libx264".into(),
|
||
|
|
"-preset".into(),
|
||
|
|
"veryfast".into(),
|
||
|
|
"-crf".into(),
|
||
|
|
"20".into(),
|
||
|
|
"-vf".into(),
|
||
|
|
"scale=trunc(iw/2)*2:trunc(ih/2)*2,format=yuv420p".into(),
|
||
|
|
"-force_key_frames".into(),
|
||
|
|
format!("expr:gte(t,n_forced*{segment_seconds})"),
|
||
|
|
"-sc_threshold".into(),
|
||
|
|
"0".into(),
|
||
|
|
],
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::{VideoEncoder, VideoPipeline};
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn libx264_profile_is_browser_compatible_and_segment_aligned() {
|
||
|
|
let args = VideoPipeline::Encode(VideoEncoder::Libx264).ffmpeg_args(2);
|
||
|
|
assert!(args.windows(2).any(|pair| pair == ["-c:v", "libx264"]));
|
||
|
|
assert!(args.windows(2).any(|pair| pair == ["-crf", "20"]));
|
||
|
|
assert!(
|
||
|
|
args.windows(2)
|
||
|
|
.any(|pair| { pair == ["-force_key_frames", "expr:gte(t,n_forced*2)"] })
|
||
|
|
);
|
||
|
|
assert!(args.iter().any(|arg| arg.contains("format=yuv420p")));
|
||
|
|
}
|
||
|
|
}
|