video-timecode 0.6.1

Library for SMPTE timecode manipulation
/// Trait describing frame rates.
pub trait FrameRate {
    const FPS: u32;

    const DROP_FRAME: bool;

    #[doc(hidden)]
    const MAX_FRAMES: u32;

    #[doc(hidden)]
    const FRAMES_PER_MINUTE: u32 = Self::FPS * 60;

    #[doc(hidden)]
    const FRAMES_PER_HOUR: u32 = Self::FRAMES_PER_MINUTE * 60;

    #[doc(hidden)]
    const DROP_FRAME_COUNT: u32 = Self::FPS / 15;

    #[doc(hidden)]
    const DISPLAY: &'static str;


    /// Given the elements of a timecode, calculate the frame offset from zero.
    #[doc(hidden)]
    fn calculate_frame_number(hour: u32, minute: u32, second: u32, frame: u32) -> Option<u32> {
        if hour > 23
            || minute > 59
            || second > 59
            || frame > Self::FPS
            || (Self::DROP_FRAME
                && second == 0
                && minute % 10 != 0
                && frame < Self::DROP_FRAME_COUNT)
        {
            return None;
        }

        let frame_number_before_drop_frames = (Self::FRAMES_PER_HOUR * hour as u32)
            + (Self::FRAMES_PER_MINUTE * minute as u32)
            + (Self::FPS * second as u32)
            + frame as u32;

        let frame_number = if Self::DROP_FRAME {
            let tens = hour * 6 + minute / 10;
            let minutes_without_tens = minute % 10;
            let drop_frame_minutes_without_tens = if minutes_without_tens > 1 {
                minutes_without_tens
            } else {
                0
            };
            let drop_frames_per_ten = Self::DROP_FRAME_COUNT * 9;
            frame_number_before_drop_frames
                - (tens * drop_frames_per_ten)
                - drop_frame_minutes_without_tens * Self::DROP_FRAME_COUNT
        } else {
            frame_number_before_drop_frames
        };

        Some(frame_number)
    }

    /// Given a frame number, calculate the fields for a time code.
    #[doc(hidden)]
    fn calculate_time_code(frame_number: u32) -> (u8, u8, u8, u8) {
        if frame_number > Self::MAX_FRAMES {
            panic!(
                "Frame rate only supports up to {:?} frames.",
                Self::MAX_FRAMES
            );
        }

        let (hour, minute, second, frame) = if Self::DROP_FRAME {
            let frames_per_drop_minute = Self::FRAMES_PER_MINUTE - Self::DROP_FRAME_COUNT;
            let frames_per_ten = Self::FRAMES_PER_MINUTE + (frames_per_drop_minute * 9);
            let frames_per_hour = frames_per_ten * 6;

            let hour = frame_number / frames_per_hour;

            let frame_number_without_hours = frame_number % frames_per_hour;
            let tens = frame_number_without_hours / frames_per_ten;
            let frame_number_without_tens = frame_number_without_hours % frames_per_ten;

            let (minute, second, frame) = if frame_number_without_tens < Self::FRAMES_PER_MINUTE {
                let minute = tens * 10;
                let frame_number_without_minutes = frame_number_without_tens;

                let second = frame_number_without_minutes / Self::FPS;

                let frame = frame_number_without_minutes % Self::FPS;

                (minute, second, frame)
            } else {
                let frame_number_without_first_minute =
                    frame_number_without_tens - Self::FRAMES_PER_MINUTE;

                let minute = 1 + (frame_number_without_first_minute / frames_per_drop_minute);

                let frame_number_without_minutes = (frame_number_without_first_minute
                    % frames_per_drop_minute)
                    + Self::DROP_FRAME_COUNT;

                let second = frame_number_without_minutes / Self::FPS;

                let frame = frame_number_without_minutes % Self::FPS;

                (tens * 10 + minute, second, frame)
            };

            (hour, minute, second, frame)
        } else {
            let total_seconds = frame_number / Self::FPS;
            let total_minutes = total_seconds / 60;

            let hour = total_minutes / 60;
            let minute = total_minutes % 60;
            let second = total_seconds % 60;
            let frame = frame_number % Self::FPS;

            (hour, minute, second, frame)
        };

        (hour as u8, minute as u8, second as u8, frame as u8)
    }
}


macro_rules! create_frame_rate {
    ($frame_rate_name:ident, $frame_rate:expr, false, $display:expr) => {
        #[derive(Clone, PartialOrd, PartialEq)]
        pub struct $frame_rate_name;

        impl FrameRate for $frame_rate_name {
            const FPS: u32 = $frame_rate;
            const DROP_FRAME: bool = false;
            const MAX_FRAMES: u32 = 86400 * Self::FPS;
            const DISPLAY: &'static str = $display;
        }
    };
    ($frame_rate_name:ident, $frame_rate:expr, $is_drop_frame:expr, $display:expr) => {
        #[derive(Clone, PartialOrd, PartialEq)]
        pub struct $frame_rate_name;

        impl FrameRate for $frame_rate_name {
            const FPS: u32 = $frame_rate;
            const DROP_FRAME: bool = $is_drop_frame;
            const MAX_FRAMES: u32 = 86400 * Self::FPS - 144 * (18 * (Self::FPS / 30));
            const DISPLAY: &'static str = $display;
        }
    };
}

create_frame_rate!(FrameRate24, 24, false, "24");
create_frame_rate!(FrameRate25, 25, false, "25");
create_frame_rate!(FrameRate30, 30, false, "30");
create_frame_rate!(FrameRate50, 50, false, "50");
create_frame_rate!(FrameRate60, 60, false, "60");
create_frame_rate!(FrameRate2398, 24, false, "23.98");
create_frame_rate!(FrameRate2997, 30, true, "29.97");
create_frame_rate!(FrameRate5994, 60, true, "59.98");