#[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 { 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 { 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"))); } }