#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SlotKind {
TimelineSlot,
StaticSlot,
EventSlot,
}
impl SlotKind {
#[must_use]
pub fn is_timeline(&self) -> bool {
matches!(self, SlotKind::TimelineSlot)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PhysicalTrack {
Video,
Audio(u8),
Timecode,
EdgeCode,
}
impl PhysicalTrack {
#[must_use]
pub fn channel(&self) -> Option<u8> {
if let PhysicalTrack::Audio(ch) = self {
Some(*ch)
} else {
None
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MobSlotDef {
pub slot_id: u32,
pub name: String,
pub kind: SlotKind,
pub physical_track: PhysicalTrack,
pub edit_rate_num: u32,
pub edit_rate_den: u32,
}
impl MobSlotDef {
#[must_use]
pub fn new(
slot_id: u32,
name: String,
kind: SlotKind,
physical_track: PhysicalTrack,
edit_rate_num: u32,
edit_rate_den: u32,
) -> Self {
Self {
slot_id,
name,
kind,
physical_track,
edit_rate_num,
edit_rate_den,
}
}
#[allow(clippy::cast_precision_loss)]
#[must_use]
pub fn edit_rate_fps(&self) -> f64 {
if self.edit_rate_den == 0 {
return 0.0;
}
self.edit_rate_num as f64 / self.edit_rate_den as f64
}
#[must_use]
pub fn is_video(&self) -> bool {
matches!(self.physical_track, PhysicalTrack::Video)
}
#[must_use]
pub fn is_audio(&self) -> bool {
matches!(self.physical_track, PhysicalTrack::Audio(_))
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct MobSlotCollection {
pub slots: Vec<MobSlotDef>,
}
impl MobSlotCollection {
#[must_use]
pub fn new() -> Self {
Self { slots: Vec::new() }
}
pub fn add(&mut self, slot: MobSlotDef) {
self.slots.push(slot);
}
#[must_use]
pub fn find_by_id(&self, id: u32) -> Option<&MobSlotDef> {
self.slots.iter().find(|s| s.slot_id == id)
}
#[must_use]
pub fn video_slots(&self) -> Vec<&MobSlotDef> {
self.slots.iter().filter(|s| s.is_video()).collect()
}
#[must_use]
pub fn audio_slots(&self) -> Vec<&MobSlotDef> {
self.slots.iter().filter(|s| s.is_audio()).collect()
}
#[must_use]
pub fn slot_count(&self) -> usize {
self.slots.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_video_slot(id: u32) -> MobSlotDef {
MobSlotDef::new(
id,
format!("V{}", id),
SlotKind::TimelineSlot,
PhysicalTrack::Video,
24,
1,
)
}
fn make_audio_slot(id: u32, ch: u8) -> MobSlotDef {
MobSlotDef::new(
id,
format!("A{}", id),
SlotKind::TimelineSlot,
PhysicalTrack::Audio(ch),
48000,
1,
)
}
#[test]
fn test_slot_kind_is_timeline() {
assert!(SlotKind::TimelineSlot.is_timeline());
assert!(!SlotKind::StaticSlot.is_timeline());
assert!(!SlotKind::EventSlot.is_timeline());
}
#[test]
fn test_physical_track_channel_video() {
assert_eq!(PhysicalTrack::Video.channel(), None);
}
#[test]
fn test_physical_track_channel_audio() {
assert_eq!(PhysicalTrack::Audio(2).channel(), Some(2));
}
#[test]
fn test_physical_track_channel_timecode() {
assert_eq!(PhysicalTrack::Timecode.channel(), None);
}
#[test]
fn test_physical_track_channel_edgecode() {
assert_eq!(PhysicalTrack::EdgeCode.channel(), None);
}
#[test]
fn test_mob_slot_def_edit_rate_fps() {
let slot = make_video_slot(1);
assert!((slot.edit_rate_fps() - 24.0).abs() < f64::EPSILON);
}
#[test]
fn test_mob_slot_def_edit_rate_zero_den() {
let slot = MobSlotDef::new(
1,
"bad".into(),
SlotKind::StaticSlot,
PhysicalTrack::Video,
25,
0,
);
assert_eq!(slot.edit_rate_fps(), 0.0);
}
#[test]
fn test_mob_slot_def_is_video() {
let slot = make_video_slot(1);
assert!(slot.is_video());
assert!(!slot.is_audio());
}
#[test]
fn test_mob_slot_def_is_audio() {
let slot = make_audio_slot(2, 1);
assert!(slot.is_audio());
assert!(!slot.is_video());
}
#[test]
fn test_mob_slot_collection_add_and_count() {
let mut col = MobSlotCollection::new();
col.add(make_video_slot(1));
col.add(make_audio_slot(2, 1));
assert_eq!(col.slot_count(), 2);
}
#[test]
fn test_mob_slot_collection_find_by_id() {
let mut col = MobSlotCollection::new();
col.add(make_video_slot(1));
col.add(make_audio_slot(2, 1));
assert!(col.find_by_id(1).is_some());
assert!(col.find_by_id(99).is_none());
}
#[test]
fn test_mob_slot_collection_video_slots() {
let mut col = MobSlotCollection::new();
col.add(make_video_slot(1));
col.add(make_audio_slot(2, 1));
col.add(make_audio_slot(3, 2));
let video = col.video_slots();
assert_eq!(video.len(), 1);
assert_eq!(video[0].slot_id, 1);
}
#[test]
fn test_mob_slot_collection_audio_slots() {
let mut col = MobSlotCollection::new();
col.add(make_video_slot(1));
col.add(make_audio_slot(2, 1));
col.add(make_audio_slot(3, 2));
let audio = col.audio_slots();
assert_eq!(audio.len(), 2);
}
#[test]
fn test_mob_slot_collection_default_is_empty() {
let col = MobSlotCollection::default();
assert_eq!(col.slot_count(), 0);
}
}