use yog_formats::video::Video;
pub(super) const ENCODER_MIN: u32 = 64;
const TO_YUV: &str = ":out_color_matrix=bt709:out_range=tv,format=yuv420p";
const EVEN: u32 = 2;
fn even(n: u32) -> u32 {
(n - n % EVEN).max(EVEN)
}
fn proportion(side: u32, across: u32, to: u32) -> u32 {
(f64::from(side) * f64::from(to) / f64::from(across.max(1))).round() as u32
}
pub(super) fn shown(v: &Video, (sw, sh): (u32, u32)) -> (u32, u32) {
let (w, h) = match (v.width, v.height) {
(Some(w), Some(h)) => (w, h),
(Some(w), None) => (w, proportion(sh, sw, w)),
(None, Some(h)) => (proportion(sw, sh, h), h),
(None, None) => (sw, sh),
};
(even(w), even(h))
}
pub(super) fn exact(v: &Video) -> Option<(u32, u32)> {
Some((even(v.width?), even(v.height?)))
}
pub(super) fn filters(v: &Video, (w, h): (u32, u32)) -> String {
let mut f = v.fps.map(|fps| format!("fps={fps},")).unwrap_or_default();
f.push_str(&format!("scale={w}:{h}{TO_YUV}"));
if w < ENCODER_MIN || h < ENCODER_MIN {
let (pw, ph) = (w.max(ENCODER_MIN), h.max(ENCODER_MIN));
f.push_str(&format!(",pad={pw}:{ph}"));
}
f
}
#[cfg(test)]
mod tests {
use super::*;
fn video(text: &str) -> Video {
yog_data::from_text::<Video>(text).unwrap().value
}
#[test]
fn one_side_keeps_the_proportion_and_sizes_are_even() {
let src = (161, 91);
assert_eq!(shown(&video(r#"{ source: "@V.a" }"#), src), (160, 90));
assert_eq!(
shown(&video(r#"{ source: "@V.a", width: 64 }"#), src),
(64, 36)
);
assert_eq!(
shown(&video(r#"{ source: "@V.a", height: 1 }"#), src),
(2, 2)
);
}
#[test]
fn a_small_picture_is_padded_up_to_the_encoders_least() {
let v = video(r#"{ source: "@V.a", fps: 12.5 }"#);
assert_eq!(
filters(&v, (320, 180)),
format!("fps=12.5,scale=320:180{TO_YUV}")
);
assert_eq!(
filters(&v, (64, 36)),
format!("fps=12.5,scale=64:36{TO_YUV},pad=64:64")
);
}
}