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