Skip to main content

dash_runtime/mpd/
inherit.rs

1//! Attribute inheritance down Period > AdaptationSet > Representation.
2//!
3//! ISO/IEC 23009-1 5.3.9.1: Segment information may be declared at any of the
4//! three levels; an element inherits the attributes and children of the same
5//! element above it, and the lower level wins attribute by attribute.
6//! `@duration` and `SegmentTimeline` are exclusive (5.3.9.2.1), so a level
7//! that introduces one does not inherit the other. A SegmentTemplate and a
8//! SegmentList may not both appear along one chain.
9//! The `RepresentationBase` attributes (5.3.7.2) of a Representation fall back
10//! to its AdaptationSet's.
11
12use thiserror::Error;
13
14use super::model::{
15    AdaptationSet, BaseInfo, CommonAttributes, MultiInfo, Period, Representation, SegmentTimeline,
16    SegmentUrl,
17};
18
19/// `@timescale` default (5.3.9.2.2).
20pub const DEFAULT_TIMESCALE: u64 = 1;
21/// `@startNumber` default (5.3.9.5.3).
22pub const DEFAULT_START_NUMBER: u64 = 1;
23/// `@presentationTimeOffset` default (5.3.9.2.2).
24pub const DEFAULT_PRESENTATION_TIME_OFFSET: u64 = 0;
25
26/// Why a Representation's Segment information could not be resolved.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
28#[non_exhaustive]
29pub enum InheritError {
30    /// A SegmentTemplate and a SegmentList along one chain (5.3.9.1).
31    #[error(
32        "SegmentTemplate and SegmentList both appear along one Period/AdaptationSet/Representation chain"
33    )]
34    TemplateAndList,
35    /// `@duration` and `SegmentTimeline` on one element (5.3.9.2.1).
36    #[error("both @duration and SegmentTimeline are present on one level")]
37    DurationAndTimeline,
38}
39
40/// The inherited multiple-Segment fields with defaults applied.
41#[derive(Debug, Clone, PartialEq)]
42#[non_exhaustive]
43pub struct ResolvedMulti {
44    /// Merged Table 16 fields (raw, defaults not applied).
45    pub base: BaseInfo,
46    /// `@timescale`, default 1.
47    pub timescale: u64,
48    /// `@presentationTimeOffset`, default 0.
49    pub presentation_time_offset: u64,
50    /// `@duration`.
51    pub duration: Option<u64>,
52    /// `@startNumber`, default 1.
53    pub start_number: u64,
54    /// `@endNumber`.
55    pub end_number: Option<u64>,
56    /// The effective `SegmentTimeline`.
57    pub timeline: Option<SegmentTimeline>,
58}
59
60/// A resolved `SegmentTemplate`.
61#[derive(Debug, Clone, PartialEq)]
62#[non_exhaustive]
63pub struct TemplateInfo {
64    /// Merged numeric fields.
65    pub multi: ResolvedMulti,
66    /// `@media`.
67    pub media: Option<String>,
68    /// `@index`.
69    pub index: Option<String>,
70    /// `@initialization`.
71    pub initialization: Option<String>,
72    /// `@bitstreamSwitching`.
73    pub bitstream_switching: Option<String>,
74}
75
76/// A resolved `SegmentList`.
77#[derive(Debug, Clone, PartialEq)]
78#[non_exhaustive]
79pub struct ListInfo {
80    /// Merged numeric fields.
81    pub multi: ResolvedMulti,
82    /// The `SegmentURL` entries (from the lowest level that lists any).
83    pub urls: Vec<SegmentUrl>,
84}
85
86/// A Representation's effective Segment information (5.3.9.1): exactly one of
87/// the three kinds.
88#[derive(Debug, Clone, PartialEq)]
89#[non_exhaustive]
90pub enum SegmentInfo {
91    /// `SegmentTemplate`.
92    Template(TemplateInfo),
93    /// `SegmentList`.
94    List(ListInfo),
95    /// `SegmentBase` or none: one Media Segment at the `BaseURL`, optionally
96    /// indexed by a `sidx` at `@indexRange`.
97    Base(BaseInfo),
98}
99
100fn merge_base(parent: &BaseInfo, child: &BaseInfo) -> BaseInfo {
101    BaseInfo {
102        timescale: child.timescale.or(parent.timescale),
103        presentation_time_offset: child
104            .presentation_time_offset
105            .or(parent.presentation_time_offset),
106        ept_delta: child.ept_delta.or(parent.ept_delta),
107        pd_delta: child.pd_delta.or(parent.pd_delta),
108        presentation_duration: child.presentation_duration.or(parent.presentation_duration),
109        time_shift_buffer_depth: child
110            .time_shift_buffer_depth
111            .or(parent.time_shift_buffer_depth),
112        index_range: child.index_range.or(parent.index_range),
113        index_range_exact: child.index_range_exact.or(parent.index_range_exact),
114        availability_time_offset: child
115            .availability_time_offset
116            .or(parent.availability_time_offset),
117        availability_time_complete: child
118            .availability_time_complete
119            .or(parent.availability_time_complete),
120        initialization: child
121            .initialization
122            .clone()
123            .or_else(|| parent.initialization.clone()),
124        representation_index: child
125            .representation_index
126            .clone()
127            .or_else(|| parent.representation_index.clone()),
128    }
129}
130
131fn merge_multi(parent: &MultiInfo, child: &MultiInfo) -> Result<MultiInfo, InheritError> {
132    if child.duration.is_some() && child.timeline.is_some() {
133        return Err(InheritError::DurationAndTimeline);
134    }
135    let (duration, timeline) = if child.timeline.is_some() {
136        (None, child.timeline.clone())
137    } else if child.duration.is_some() {
138        (child.duration, None)
139    } else {
140        (parent.duration, parent.timeline.clone())
141    };
142    Ok(MultiInfo {
143        base: merge_base(&parent.base, &child.base),
144        duration,
145        start_number: child.start_number.or(parent.start_number),
146        end_number: child.end_number.or(parent.end_number),
147        timeline,
148        bitstream_switching: child
149            .bitstream_switching
150            .clone()
151            .or_else(|| parent.bitstream_switching.clone()),
152    })
153}
154
155fn finish_multi(m: MultiInfo) -> Result<ResolvedMulti, InheritError> {
156    if m.duration.is_some() && m.timeline.is_some() {
157        return Err(InheritError::DurationAndTimeline);
158    }
159    Ok(ResolvedMulti {
160        timescale: m.base.timescale.unwrap_or(DEFAULT_TIMESCALE),
161        presentation_time_offset: m
162            .base
163            .presentation_time_offset
164            .unwrap_or(DEFAULT_PRESENTATION_TIME_OFFSET),
165        base: m.base,
166        duration: m.duration,
167        start_number: m.start_number.unwrap_or(DEFAULT_START_NUMBER),
168        end_number: m.end_number,
169        timeline: m.timeline,
170    })
171}
172
173/// Resolve the effective Segment information of `rep`.
174///
175/// # Errors
176/// [`InheritError`] when the chain violates 5.3.9.1 / 5.3.9.2.1; the caller
177/// drops the Representation.
178pub fn resolve_segment_info(
179    period: &Period,
180    set: &AdaptationSet,
181    rep: &Representation,
182) -> Result<SegmentInfo, InheritError> {
183    let layers = [&period.segment, &set.segment, &rep.segment];
184    let has_template = layers.iter().any(|l| l.template.is_some());
185    let has_list = layers.iter().any(|l| l.list.is_some());
186    if has_template && has_list {
187        return Err(InheritError::TemplateAndList);
188    }
189    if has_template {
190        let mut merged = crate::mpd::model::SegmentTemplate::default();
191        for t in layers.iter().filter_map(|l| l.template.as_ref()) {
192            merged = crate::mpd::model::SegmentTemplate {
193                multi: merge_multi(&merged.multi, &t.multi)?,
194                media: t.media.clone().or(merged.media),
195                index: t.index.clone().or(merged.index),
196                initialization: t.initialization.clone().or(merged.initialization),
197                bitstream_switching: t.bitstream_switching.clone().or(merged.bitstream_switching),
198            };
199        }
200        return Ok(SegmentInfo::Template(TemplateInfo {
201            multi: finish_multi(merged.multi)?,
202            media: merged.media,
203            index: merged.index,
204            initialization: merged.initialization,
205            bitstream_switching: merged.bitstream_switching,
206        }));
207    }
208    if has_list {
209        let mut multi = MultiInfo::default();
210        let mut urls: Vec<SegmentUrl> = Vec::new();
211        for l in layers.iter().filter_map(|l| l.list.as_ref()) {
212            multi = merge_multi(&multi, &l.multi)?;
213            if !l.urls.is_empty() {
214                urls.clone_from(&l.urls);
215            }
216        }
217        return Ok(SegmentInfo::List(ListInfo {
218            multi: finish_multi(multi)?,
219            urls,
220        }));
221    }
222    let mut base = BaseInfo::default();
223    for b in layers.iter().filter_map(|l| l.base.as_ref()) {
224        base = merge_base(&base, &b.info);
225    }
226    Ok(SegmentInfo::Base(base))
227}
228
229/// The `RepresentationBase` attributes of `rep`, each falling back to `set`.
230#[must_use]
231pub fn effective_common(set: &AdaptationSet, rep: &Representation) -> CommonAttributes {
232    let (s, r) = (&set.common, &rep.common);
233    CommonAttributes {
234        profiles: r.profiles.clone().or_else(|| s.profiles.clone()),
235        width: r.width.or(s.width),
236        height: r.height.or(s.height),
237        sar: r.sar.clone().or_else(|| s.sar.clone()),
238        frame_rate: r.frame_rate.clone().or_else(|| s.frame_rate.clone()),
239        audio_sampling_rate: r
240            .audio_sampling_rate
241            .clone()
242            .or_else(|| s.audio_sampling_rate.clone()),
243        mime_type: r.mime_type.clone().or_else(|| s.mime_type.clone()),
244        codecs: r.codecs.clone().or_else(|| s.codecs.clone()),
245        max_playout_rate: r.max_playout_rate.or(s.max_playout_rate),
246    }
247}