Skip to main content

dash_runtime/address/
list.rs

1//! `SegmentList` entries (ISO/IEC 23009-1 5.3.9.3, Table 19).
2
3use super::AddressError;
4use super::timeline::TimelineIndex;
5use broadcast_dash::inherit::ListInfo;
6use broadcast_dash::{ByteRange, SegmentUrl};
7
8/// One listed segment with its timing.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub struct ListSegment {
12    /// Segment number.
13    pub number: u64,
14    /// Start on the timeline in ticks: `i * @duration`, or the timeline's `t`.
15    pub time: u64,
16    /// Duration in ticks.
17    pub duration: u64,
18    /// `@media` reference (empty: the BaseURL itself).
19    pub reference: String,
20    /// `@mediaRange`.
21    pub range: Option<ByteRange>,
22    /// The entry's own `SegmentURL`.
23    pub entry: SegmentUrl,
24}
25
26/// Expand a `SegmentList`. `period_duration_ticks` shortens the last segment
27/// when the Period ends inside it (5.3.9.5.3).
28///
29/// # Errors
30/// [`AddressError`] when entries cannot be timed or are empty.
31pub fn expand(
32    info: &ListInfo,
33    timeline: Option<&TimelineIndex>,
34    period_duration_ticks: Option<u64>,
35) -> Result<Vec<ListSegment>, AddressError> {
36    let urls = &info.urls;
37    let multi = &info.multi;
38    let mut out = Vec::with_capacity(urls.len());
39    if let Some(t) = timeline {
40        if t.len() != urls.len() as u64 {
41            return Err(AddressError::ListTimelineMismatch {
42                urls: urls.len(),
43                segments: t.len(),
44            });
45        }
46    } else if multi.duration.is_none() && urls.len() > 1 {
47        return Err(AddressError::MissingDuration);
48    }
49    for (i, entry) in urls.iter().enumerate() {
50        if entry.media.is_none() && entry.media_range.is_none() {
51            return Err(AddressError::EmptySegmentUrl);
52        }
53        let idx = i as u64;
54        let (number, time, duration) = match timeline.and_then(|t| t.segment(idx)) {
55            Some(seg) => (seg.number, seg.time, seg.duration),
56            None => {
57                let d = multi.duration.unwrap_or(period_duration_ticks.unwrap_or(0));
58                let start = idx.checked_mul(d).ok_or(AddressError::Overflow)?;
59                let last = i + 1 == urls.len();
60                let dur = match (last, multi.duration, period_duration_ticks) {
61                    (true, Some(nominal), Some(total)) => total.saturating_sub(start).min(nominal),
62                    _ => d,
63                };
64                (multi.start_number.saturating_add(idx), start, dur)
65            }
66        };
67        out.push(ListSegment {
68            number,
69            time,
70            duration,
71            reference: entry.media.clone().unwrap_or_default(),
72            range: entry.media_range,
73            entry: entry.clone(),
74        });
75    }
76    Ok(out)
77}