use std::cmp::Ordering;
use crate::mp4::mux::track::TrackState;
fn compare(a: (i64, u32), b: (i64, u32)) -> Ordering {
(i128::from(a.0) * i128::from(b.1)).cmp(&(i128::from(b.0) * i128::from(a.1)))
}
fn earliest_closed(tracks: &[TrackState]) -> Option<(usize, (i64, u32))> {
let mut best: Option<(usize, (i64, u32))> = None;
for (slot, track) in tracks.iter().enumerate() {
let Some(chunk) = track.closed.front() else {
continue;
};
let time = (chunk.first_dts, track.spec.timescale);
if best.is_none_or(|(_, best_time)| compare(time, best_time) == Ordering::Less) {
best = Some((slot, time));
}
}
best
}
pub(crate) fn next_chunk(tracks: &[TrackState], force: bool) -> Option<usize> {
let (slot, time) = earliest_closed(tracks)?;
if force {
return Some(slot);
}
for (other, track) in tracks.iter().enumerate() {
if other == slot {
continue;
}
let bound = track.lower_bound()?;
if compare((bound, track.spec.timescale), time) == Ordering::Less {
return None;
}
}
Some(slot)
}
pub(crate) fn earliest_open(tracks: &[TrackState]) -> Option<usize> {
let mut best: Option<(usize, (i64, u32))> = None;
for (slot, track) in tracks.iter().enumerate() {
let Some(chunk) = &track.open else {
continue;
};
let time = (chunk.first_dts, track.spec.timescale);
if best.is_none_or(|(_, best_time)| compare(time, best_time) == Ordering::Less) {
best = Some((slot, time));
}
}
best.map(|(slot, _)| slot)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compare_is_exact_across_timescales() {
assert_eq!(compare((1, 3), (333_333, 1_000_000)), Ordering::Greater);
assert_eq!(compare((1, 2), (500, 1000)), Ordering::Equal);
assert_eq!(compare((-1, 2), (0, 1000)), Ordering::Less);
}
}