talw-timecode 0.1.0

SMPTE timecode arithmetic — parse, format, convert, drop-frame
Documentation
use crate::framerate::FrameRate;

pub fn convert_frames(total_frames: i64, from: FrameRate, to: FrameRate) -> i64 {
    if from == to {
        return total_frames;
    }

    let from_r = from.rational();
    let to_r = to.rational();

    // frames * (from_den / from_num) gives seconds
    // seconds * (to_num / to_den) gives target frames
    // Combined: frames * from_den * to_num / (from_num * to_den)
    let numerator = total_frames as i128 * from_r.den as i128 * to_r.num as i128;
    let denominator = from_r.num as i128 * to_r.den as i128;

    (numerator / denominator) as i64
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn same_rate() {
        assert_eq!(convert_frames(100, FrameRate::Fps24, FrameRate::Fps24), 100);
    }

    #[test]
    fn fps24_to_fps30() {
        assert_eq!(convert_frames(24, FrameRate::Fps24, FrameRate::Fps30), 30);
    }

    #[test]
    fn fps24_to_fps25() {
        assert_eq!(convert_frames(24, FrameRate::Fps24, FrameRate::Fps25), 25);
    }

    #[test]
    fn fps30_to_fps24() {
        assert_eq!(convert_frames(30, FrameRate::Fps30, FrameRate::Fps24), 24);
    }

    #[test]
    fn large_value() {
        // 1 hour at 24fps = 86400 frames -> 1 hour at 60fps = 216000 frames
        assert_eq!(
            convert_frames(86400, FrameRate::Fps24, FrameRate::Fps60),
            216000
        );
    }
}