use crate::limits::MAX_TIMELINE_SEGMENTS;
use crate::mpd::model::SegmentTimeline;
use super::AddressError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct TimelineSegment {
pub number: u64,
pub time: u64,
pub duration: u64,
}
#[derive(Debug, Clone, Copy)]
struct Run {
first_index: u64,
time: u64,
number: u64,
d: u64,
count: u64,
}
#[derive(Debug, Clone, Default)]
pub struct TimelineIndex {
runs: Vec<Run>,
total: u64,
}
const TIMELINE_T_TOLERANCE_TICKS: u64 = 1;
impl TimelineIndex {
pub fn build(
timeline: &SegmentTimeline,
start_number: u64,
period_end_ticks: Option<u64>,
) -> Result<Self, AddressError> {
if let Some(u) = timeline.unsupported {
return Err(AddressError::Unsupported(u));
}
let entries = &timeline.entries;
let mut runs = Vec::with_capacity(entries.len());
let mut time: u64 = 0;
let mut number = start_number;
let mut total: u64 = 0;
for (i, s) in entries.iter().enumerate() {
if s.d == 0 {
return Err(AddressError::ZeroSegmentDuration);
}
if let Some(t) = s.t {
if time.saturating_sub(t) > TIMELINE_T_TOLERANCE_TICKS {
return Err(AddressError::TimelineNotIncreasing { t, floor: time });
}
time = t;
}
if let Some(n) = s.n {
if n < number {
return Err(AddressError::NumberNotIncreasing {
n,
expected: number,
});
}
number = n;
}
let count = if s.r >= 0 {
s.r.unsigned_abs().saturating_add(1)
} else {
let next_t = entries.get(i + 1).and_then(|n| n.t);
if let Some(next_t) = next_t
&& time.saturating_sub(next_t) > TIMELINE_T_TOLERANCE_TICKS
{
return Err(AddressError::TimelineNotIncreasing {
t: next_t,
floor: time,
});
}
match next_t.or(period_end_ticks) {
Some(end) => end.saturating_sub(time).div_ceil(s.d),
None => 1,
}
};
if count > MAX_TIMELINE_SEGMENTS || total.saturating_add(count) > MAX_TIMELINE_SEGMENTS
{
return Err(AddressError::TimelineTooLong {
count_hint: total.saturating_add(count),
});
}
if count > 0 {
runs.push(Run {
first_index: total,
time,
number,
d: s.d,
count,
});
}
let span = s.d.checked_mul(count).ok_or(AddressError::Overflow)?;
let cut_at_next = s.r < 0 && entries.get(i + 1).is_some_and(|n| n.t.is_some());
time = if cut_at_next {
entries[i + 1].t.unwrap_or(time)
} else {
time.checked_add(span).ok_or(AddressError::Overflow)?
};
number = number.saturating_add(count);
total += count;
}
Ok(Self { runs, total })
}
#[must_use]
pub fn len(&self) -> u64 {
self.total
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.total == 0
}
#[must_use]
pub fn segment(&self, index: u64) -> Option<TimelineSegment> {
if index >= self.total {
return None;
}
let at = self.runs.partition_point(|r| r.first_index <= index) - 1;
let run = self.runs[at];
let k = index - run.first_index;
Some(TimelineSegment {
number: run.number.saturating_add(k),
time: run.time.saturating_add(k.saturating_mul(run.d)),
duration: run.d,
})
}
#[must_use]
pub fn index_at_or_before(&self, time: u64) -> Option<u64> {
let at = self.runs.partition_point(|r| r.time <= time);
if at == 0 {
return None;
}
let run = self.runs[at - 1];
let k = ((time - run.time) / run.d).min(run.count - 1);
Some(run.first_index + k)
}
#[must_use]
pub fn count_up_to_number(&self, max_number: u64) -> u64 {
let mut count = 0u64;
for run in &self.runs {
if run.number > max_number {
break;
}
let fit = (max_number - run.number).saturating_add(1);
count += fit.min(run.count);
if fit < run.count {
break;
}
}
count
}
pub fn iter(&self) -> impl Iterator<Item = TimelineSegment> + '_ {
(0..self.total).filter_map(|i| self.segment(i))
}
}