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 8.16.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
6//! 8.16.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 crate::mpd::model::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            continue;
70        }
71        let size = u64::from(r.referenced_size);
72        let last = offset
73            .checked_add(size)
74            .and_then(|e| e.checked_sub(1))
75            .ok_or(AddressError::Overflow)?;
76        let duration = u64::from(r.subsegment_duration);
77        out.push(IndexedSegment {
78            time,
79            duration,
80            range: ByteRange::new(offset, last),
81            starts_with_sap: r.starts_with_sap != 0,
82        });
83        offset = last.checked_add(1).ok_or(AddressError::Overflow)?;
84        time = time.checked_add(duration).ok_or(AddressError::Overflow)?;
85    }
86    Ok(out)
87}