Skip to main content

dash_runtime/address/
base_index.rs

1//! SegmentBase + `sidx`: the on-demand profile's segment ranges
2//! (ISO/IEC 23009-1 5.3.9.5.4; ISO/IEC 14496-12:2026 8.14.3).
3//!
4//! The Representation is one file; `@indexRange` locates its `sidx` box. The
5//! sidx offsets are anchored at the first byte after the box (14496-12:2026
6//! 8.14.3), so each reference's byte range follows from the box alone.
7
8use broadcast_common::Parse;
9use transmux::SegmentIndexBox;
10
11use super::AddressError;
12use crate::error::Unsupported;
13use broadcast_dash::ByteRange;
14
15/// `reference_type` of a reference that points at another `sidx`.
16const REFERENCE_TYPE_SIDX: u8 = 1;
17
18/// One subsegment described by a `sidx`.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[non_exhaustive]
21pub struct IndexedSegment {
22    /// Earliest presentation time on the sidx timescale.
23    pub time: u64,
24    /// Duration in sidx timescale ticks.
25    pub duration: u64,
26    /// The byte range of the subsegment in the file.
27    pub range: ByteRange,
28    /// `starts_with_SAP`.
29    pub starts_with_sap: bool,
30}
31
32/// The anchor offset: the first byte after the index box, i.e.
33/// `indexRange.last + 1`.
34///
35/// # Errors
36/// [`AddressError::Unsupported`] for an open-ended range.
37pub fn anchor_after(index_range: &ByteRange) -> Result<u64, AddressError> {
38    index_range
39        .last
40        .and_then(|l| l.checked_add(1))
41        .ok_or(AddressError::Unsupported(Unsupported::OpenEndedRange))
42}
43
44/// Parse the bytes of `@indexRange` (the whole `sidx` box, header included).
45///
46/// # Errors
47/// [`AddressError::Sidx`] when the bytes are not a `sidx` box.
48pub fn parse_index(bytes: &[u8]) -> Result<SegmentIndexBox, AddressError> {
49    SegmentIndexBox::parse(bytes).map_err(|e| AddressError::Sidx(e.to_string()))
50}
51
52/// The subsegments of `sidx`, with byte ranges relative to the file start.
53/// `anchor` is [`anchor_after`] of the `@indexRange`.
54///
55/// # Errors
56/// [`AddressError::Unsupported`] for a hierarchical index;
57/// [`AddressError::Overflow`] for sizes that overflow a file offset.
58pub fn segments(sidx: &SegmentIndexBox, anchor: u64) -> Result<Vec<IndexedSegment>, AddressError> {
59    let mut offset = anchor
60        .checked_add(sidx.first_offset)
61        .ok_or(AddressError::Overflow)?;
62    let mut time = sidx.earliest_presentation_time;
63    let mut out = Vec::with_capacity(sidx.references.len());
64    for r in &sidx.references {
65        if r.reference_type == REFERENCE_TYPE_SIDX {
66            return Err(AddressError::Unsupported(Unsupported::HierarchicalSidx));
67        }
68        if r.referenced_size == 0 {
69            time = time
70                .checked_add(u64::from(r.subsegment_duration))
71                .ok_or(AddressError::Overflow)?;
72            continue;
73        }
74        let size = u64::from(r.referenced_size);
75        let last = offset
76            .checked_add(size)
77            .and_then(|e| e.checked_sub(1))
78            .ok_or(AddressError::Overflow)?;
79        let duration = u64::from(r.subsegment_duration);
80        out.push(IndexedSegment {
81            time,
82            duration,
83            range: ByteRange::new(offset, last),
84            starts_with_sap: r.starts_with_sap != 0,
85        });
86        offset = last.checked_add(1).ok_or(AddressError::Overflow)?;
87        time = time.checked_add(duration).ok_or(AddressError::Overflow)?;
88    }
89    Ok(out)
90}