use crate::errors::FramerateParseError;
use crate::framerate_parse::FramerateSource;
use num::ToPrimitive;
use std::fmt;
use std::fmt::Formatter;
#[allow(unused)] use num::Rational64;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Ntsc {
None,
NonDropFrame,
DropFrame,
}
impl Ntsc {
pub fn is_ntsc(self) -> bool {
self != Self::None
}
}
impl fmt::Display for Ntsc {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let ntsc_str = match self {
Ntsc::None => "",
Ntsc::NonDropFrame => "NTSC NDF",
Ntsc::DropFrame => "NTSC DF",
};
write!(f, "{}", ntsc_str)
}
}
pub type FramerateParseResult = Result<Framerate, FramerateParseError>;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Framerate {
value: num::Rational64,
ntsc: Ntsc,
}
impl Framerate {
pub fn playback(&self) -> num::Rational64 {
self.value
}
pub fn timebase(&self) -> num::Rational64 {
if self.ntsc.is_ntsc() {
return self.value.round();
}
self.value
}
pub fn ntsc(&self) -> Ntsc {
self.ntsc
}
pub(crate) fn drop_frames_per_minute(&self) -> Option<i64> {
if self.ntsc != Ntsc::DropFrame {
return None;
}
let drop_frames = self.timebase().round().to_integer() as f64 * 0.066666;
Some(drop_frames.round() as i64)
}
pub fn with_playback<T: FramerateSource>(rate: T, ntsc: Ntsc) -> FramerateParseResult {
let rational = rate.to_playback(ntsc, false)?;
let rate = Framerate {
value: rational,
ntsc,
};
Ok(rate)
}
pub fn with_timebase<T: FramerateSource>(base: T, ntsc: Ntsc) -> FramerateParseResult {
let rational = base.to_playback(ntsc, true)?;
let rate = Framerate {
value: rational,
ntsc,
};
Ok(rate)
}
}
impl fmt::Display for Framerate {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let value_str = format!("{:.2}", self.value.to_f64().unwrap());
let mut value_str = value_str.trim_end_matches('0');
value_str = value_str.trim_end_matches('.');
write!(f, "[{}", value_str)?;
if self.ntsc.is_ntsc() {
write!(f, " ")?;
}
write!(f, "{}]", self.ntsc)
}
}
pub mod rates {
use crate::Framerate;
use crate::Ntsc;
pub const F23_98: Framerate = Framerate {
value: num::Rational64::new_raw(24000, 1001),
ntsc: Ntsc::NonDropFrame,
};
pub const F24: Framerate = Framerate {
value: num::Rational64::new_raw(24, 1),
ntsc: Ntsc::None,
};
pub const F25: Framerate = Framerate {
value: num::Rational64::new_raw(25, 1),
ntsc: Ntsc::None,
};
pub const F29_97_NDF: Framerate = Framerate {
value: num::Rational64::new_raw(30000, 1001),
ntsc: Ntsc::NonDropFrame,
};
pub const F29_97_DF: Framerate = Framerate {
value: num::Rational64::new_raw(30000, 1001),
ntsc: Ntsc::DropFrame,
};
pub const F30: Framerate = Framerate {
value: num::Rational64::new_raw(30, 1),
ntsc: Ntsc::None,
};
pub const F47_95: Framerate = Framerate {
value: num::Rational64::new_raw(48000, 1001),
ntsc: Ntsc::NonDropFrame,
};
pub const F48: Framerate = Framerate {
value: num::Rational64::new_raw(48, 1),
ntsc: Ntsc::None,
};
pub const F59_94_NDF: Framerate = Framerate {
value: num::Rational64::new_raw(60000, 1001),
ntsc: Ntsc::NonDropFrame,
};
pub const F59_94_DF: Framerate = Framerate {
value: num::Rational64::new_raw(60000, 1001),
ntsc: Ntsc::DropFrame,
};
pub const F60: Framerate = Framerate {
value: num::Rational64::new_raw(60, 1),
ntsc: Ntsc::None,
};
}