Skip to main content

dash_runtime/mpd/
model.rs

1//! The consume-only MPD model (ISO/IEC 23009-1 clause 5.3, Annex B schema).
2//!
3//! Every field is typed; nothing keeps source text for a later passthrough.
4//! Times are Unix microseconds (`*_us`, `i64`) or [`Duration`]. Element
5//! content the core does not act on (`EventStream`, `Preselection`, ...) is
6//! recorded only as a flag in [`MpdFeatures`] so a caller can report it.
7
8use std::time::Duration;
9
10use broadcast_common::impl_spec_display;
11
12use crate::error::Unsupported;
13use crate::timing::utc_timing::UtcScheme;
14
15/// `MPD@type` (Table 3).
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17#[non_exhaustive]
18pub enum PresentationType {
19    /// `static` (the default): the whole presentation is available.
20    #[default]
21    Static,
22    /// `dynamic`: segments become available over wall-clock time.
23    Dynamic,
24}
25
26impl PresentationType {
27    /// Stable label (the attribute token).
28    #[must_use]
29    pub fn name(&self) -> &'static str {
30        match self {
31            Self::Static => "static",
32            Self::Dynamic => "dynamic",
33        }
34    }
35}
36impl_spec_display!(PresentationType);
37
38/// `@xlink:actuate` (5.5.3).
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
40#[non_exhaustive]
41pub enum Actuate {
42    /// Resolve when the MPD is processed.
43    OnLoad,
44    /// Resolve when needed (the default).
45    #[default]
46    OnRequest,
47}
48
49impl Actuate {
50    /// Stable label (the attribute token).
51    #[must_use]
52    pub fn name(&self) -> &'static str {
53        match self {
54            Self::OnLoad => "onLoad",
55            Self::OnRequest => "onRequest",
56        }
57    }
58}
59impl_spec_display!(Actuate);
60
61/// An `xlink` reference on a Period, AdaptationSet or SegmentList.
62#[derive(Debug, Clone, PartialEq, Eq)]
63#[non_exhaustive]
64pub struct Xlink {
65    /// `@xlink:href`.
66    pub href: String,
67    /// `@xlink:actuate`.
68    pub actuate: Actuate,
69}
70
71/// A single byte range `first-last` (RFC 7233 2.1), inclusive.
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73#[non_exhaustive]
74pub struct ByteRange {
75    /// First byte.
76    pub first: u64,
77    /// Last byte; `None` for the open-ended form `first-`.
78    pub last: Option<u64>,
79}
80
81impl ByteRange {
82    /// A closed range.
83    #[must_use]
84    pub fn new(first: u64, last: u64) -> Self {
85        Self {
86            first,
87            last: Some(last),
88        }
89    }
90
91    /// Number of bytes; `None` when open-ended or `last < first`.
92    #[must_use]
93    pub fn len(&self) -> Option<u64> {
94        let last = self.last?;
95        last.checked_sub(self.first)?.checked_add(1)
96    }
97
98    /// Whether the range is empty or open-ended (no byte count).
99    #[must_use]
100    pub fn is_empty(&self) -> bool {
101        self.len().is_none()
102    }
103
104    /// The closed `(offset, length)` form, or the typed unsupported error for
105    /// an open-ended range.
106    ///
107    /// # Errors
108    /// [`Unsupported::OpenEndedRange`].
109    pub fn offset_len(&self) -> Result<(u64, u64), Unsupported> {
110        self.len()
111            .map(|n| (self.first, n))
112            .ok_or(Unsupported::OpenEndedRange)
113    }
114}
115
116/// `URLType` (Table 18): an `Initialization`/`RepresentationIndex` address.
117#[derive(Debug, Clone, PartialEq, Eq, Default)]
118#[non_exhaustive]
119pub struct UrlType {
120    /// `@sourceURL`; absent means the BaseURL itself.
121    pub source_url: Option<String>,
122    /// `@range`.
123    pub range: Option<ByteRange>,
124}
125
126/// A `BaseURL` (Table 30) with the DVB extension attributes.
127#[derive(Debug, Clone, PartialEq, Default)]
128#[non_exhaustive]
129pub struct BaseUrl {
130    /// Element text.
131    pub url: String,
132    /// `@serviceLocation`.
133    pub service_location: Option<String>,
134    /// `@dvb:priority` (ETSI TS 103 285 10.8.2.1).
135    pub priority: Option<u32>,
136    /// `@dvb:weight`; parsed, never used (the core is deterministic).
137    pub weight: Option<u32>,
138    /// `@availabilityTimeOffset`, seconds.
139    pub availability_time_offset: Option<f64>,
140    /// `@availabilityTimeComplete`.
141    pub availability_time_complete: Option<bool>,
142    /// `@timeShiftBufferDepth`.
143    pub time_shift_buffer_depth: Option<Duration>,
144}
145
146/// A generic descriptor (5.8.2): `Role`, `Accessibility`,
147/// `EssentialProperty`, `SupplementalProperty`, `ContentProtection`, ...
148#[derive(Debug, Clone, PartialEq, Eq, Default)]
149#[non_exhaustive]
150pub struct Descriptor {
151    /// `@schemeIdUri`.
152    pub scheme_id_uri: String,
153    /// `@value`.
154    pub value: Option<String>,
155    /// `@id`.
156    pub id: Option<String>,
157    /// `@cenc:default_KID` on `ContentProtection`.
158    pub default_kid: Option<String>,
159    /// The `UrlQueryInfo` child of an `urn:mpeg:dash:urlparam:2014` property.
160    pub url_query: Option<UrlQueryInfo>,
161}
162
163/// `up:UrlQueryInfo` (Annex I.2).
164#[derive(Debug, Clone, PartialEq, Eq, Default)]
165#[non_exhaustive]
166pub struct UrlQueryInfo {
167    /// `@useMPDUrlQuery`.
168    pub use_mpd_url_query: bool,
169    /// `@queryString`, literal.
170    pub query_string: Option<String>,
171    /// `@queryTemplate`; present means placeholders this core does not
172    /// understand.
173    pub query_template: Option<String>,
174}
175
176/// A `UTCTiming` element (5.8.4.11).
177#[derive(Debug, Clone, PartialEq, Eq)]
178#[non_exhaustive]
179pub struct UtcTiming {
180    /// Classified `@schemeIdUri`.
181    pub scheme: UtcScheme,
182    /// `@schemeIdUri`.
183    pub scheme_id_uri: String,
184    /// `@value`: the time itself for `direct`, else whitespace-separated URLs.
185    pub value: String,
186    /// `@id`.
187    pub id: Option<String>,
188}
189
190impl UtcTiming {
191    /// The whitespace-separated entries of `@value`.
192    pub fn values(&self) -> impl Iterator<Item = &str> {
193        self.value.split_ascii_whitespace()
194    }
195}
196
197/// Which not-acted-on constructs the MPD contained.
198#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
199#[non_exhaustive]
200pub struct MpdFeatures {
201    /// `PatchLocation` (5.15).
202    pub patch_location: bool,
203    /// `EventStream` (5.10.2).
204    pub event_stream: bool,
205    /// `InbandEventStream` (5.10.3).
206    pub inband_event_stream: bool,
207    /// `Preselection` (5.3.11).
208    pub preselection: bool,
209    /// `ServiceDescription` (Annex K).
210    pub service_description: bool,
211    /// `ProducerReferenceTime` (5.12).
212    pub producer_reference_time: bool,
213    /// `ContentSteering`.
214    pub content_steering: bool,
215    /// `Resync` (5.3.13).
216    pub resync: bool,
217    /// An `availabilityTimeOffset`, or `availabilityTimeComplete="false"`,
218    /// anywhere: a low-latency presentation.
219    pub low_latency: bool,
220    /// A Period or AdaptationSet removed because its xlink was
221    /// `urn:mpeg:dash:resolve-to-zero:2013`.
222    pub xlink_resolve_to_zero: bool,
223}
224
225/// The `RepresentationBase` attributes (5.3.7.2) that AdaptationSet and
226/// Representation both carry and Representation inherits.
227#[derive(Debug, Clone, PartialEq, Default)]
228#[non_exhaustive]
229pub struct CommonAttributes {
230    /// `@profiles`.
231    pub profiles: Option<String>,
232    /// `@width`.
233    pub width: Option<u32>,
234    /// `@height`.
235    pub height: Option<u32>,
236    /// `@sar`.
237    pub sar: Option<String>,
238    /// `@frameRate` (`n` or `n/d`).
239    pub frame_rate: Option<String>,
240    /// `@audioSamplingRate` (`n` or `n n`).
241    pub audio_sampling_rate: Option<String>,
242    /// `@mimeType`.
243    pub mime_type: Option<String>,
244    /// `@codecs`.
245    pub codecs: Option<String>,
246    /// `@maxPlayoutRate`.
247    pub max_playout_rate: Option<f64>,
248}
249
250/// `SegmentBase` / the Segment Base Information fields (Table 16).
251#[derive(Debug, Clone, PartialEq, Default)]
252#[non_exhaustive]
253pub struct BaseInfo {
254    /// `@timescale`.
255    pub timescale: Option<u64>,
256    /// `@presentationTimeOffset`.
257    pub presentation_time_offset: Option<u64>,
258    /// `@eptDelta`.
259    pub ept_delta: Option<i64>,
260    /// `@pdDelta`.
261    pub pd_delta: Option<i64>,
262    /// `@presentationDuration`.
263    pub presentation_duration: Option<u64>,
264    /// `@timeShiftBufferDepth`.
265    pub time_shift_buffer_depth: Option<Duration>,
266    /// `@indexRange`.
267    pub index_range: Option<ByteRange>,
268    /// `@indexRangeExact`.
269    pub index_range_exact: Option<bool>,
270    /// `@availabilityTimeOffset`, seconds (may be `INF`).
271    pub availability_time_offset: Option<f64>,
272    /// `@availabilityTimeComplete`.
273    pub availability_time_complete: Option<bool>,
274    /// `Initialization`.
275    pub initialization: Option<UrlType>,
276    /// `RepresentationIndex`.
277    pub representation_index: Option<UrlType>,
278}
279
280/// `SegmentBase` element.
281#[derive(Debug, Clone, PartialEq, Default)]
282#[non_exhaustive]
283pub struct SegmentBase {
284    /// The Table 16 fields.
285    pub info: BaseInfo,
286}
287
288/// One `S` of a `SegmentTimeline` (Table 22).
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290#[non_exhaustive]
291pub struct TimelineEntry {
292    /// `@t`.
293    pub t: Option<u64>,
294    /// `@n`.
295    pub n: Option<u64>,
296    /// `@d`.
297    pub d: u64,
298    /// `@r` (negative: until the next `S@t` or the Period end).
299    pub r: i64,
300    /// `@k`.
301    pub k: Option<u64>,
302}
303
304impl TimelineEntry {
305    /// An entry with only `d` set (`t`, `n`, `k` absent, `r` 0).
306    #[must_use]
307    pub fn new(d: u64) -> Self {
308        Self {
309            t: None,
310            n: None,
311            d,
312            r: 0,
313            k: None,
314        }
315    }
316}
317
318/// `SegmentTimeline` (5.3.9.6).
319#[derive(Debug, Clone, PartialEq, Eq, Default)]
320#[non_exhaustive]
321pub struct SegmentTimeline {
322    /// The `S` entries in document order.
323    pub entries: Vec<TimelineEntry>,
324    /// Set when the timeline uses a feature outside 0.1 (`S@k` above 1, an
325    /// Ed.6 `Pattern`); addressing then fails with that typed error.
326    pub unsupported: Option<Unsupported>,
327}
328
329/// The Multiple Segment Base Information fields (Table 17) over [`BaseInfo`].
330#[derive(Debug, Clone, PartialEq, Default)]
331#[non_exhaustive]
332pub struct MultiInfo {
333    /// The Table 16 fields.
334    pub base: BaseInfo,
335    /// `@duration`.
336    pub duration: Option<u64>,
337    /// `@startNumber`.
338    pub start_number: Option<u64>,
339    /// `@endNumber`.
340    pub end_number: Option<u64>,
341    /// `SegmentTimeline`.
342    pub timeline: Option<SegmentTimeline>,
343    /// `BitstreamSwitching`.
344    pub bitstream_switching: Option<UrlType>,
345}
346
347/// `SegmentTemplate` (5.3.9.4).
348#[derive(Debug, Clone, PartialEq, Default)]
349#[non_exhaustive]
350pub struct SegmentTemplate {
351    /// Table 16/17 fields.
352    pub multi: MultiInfo,
353    /// `@media` template.
354    pub media: Option<String>,
355    /// `@index` template.
356    pub index: Option<String>,
357    /// `@initialization` template.
358    pub initialization: Option<String>,
359    /// `@bitstreamSwitching` template.
360    pub bitstream_switching: Option<String>,
361}
362
363/// One `SegmentURL` (Table 19).
364#[derive(Debug, Clone, PartialEq, Eq, Default)]
365#[non_exhaustive]
366pub struct SegmentUrl {
367    /// `@media`.
368    pub media: Option<String>,
369    /// `@mediaRange`.
370    pub media_range: Option<ByteRange>,
371    /// `@index`.
372    pub index: Option<String>,
373    /// `@indexRange`.
374    pub index_range: Option<ByteRange>,
375}
376
377/// `SegmentList` (5.3.9.3).
378#[derive(Debug, Clone, PartialEq, Default)]
379#[non_exhaustive]
380pub struct SegmentList {
381    /// Table 16/17 fields.
382    pub multi: MultiInfo,
383    /// `@xlink:href`.
384    pub xlink: Option<Xlink>,
385    /// The `SegmentURL` entries.
386    pub urls: Vec<SegmentUrl>,
387}
388
389/// The Segment information declared at one hierarchy level: at most one of
390/// the three kinds (5.3.9.1).
391#[derive(Debug, Clone, PartialEq, Default)]
392#[non_exhaustive]
393pub struct SegmentLayer {
394    /// `SegmentBase`.
395    pub base: Option<SegmentBase>,
396    /// `SegmentList`.
397    pub list: Option<SegmentList>,
398    /// `SegmentTemplate`.
399    pub template: Option<SegmentTemplate>,
400}
401
402/// A `Representation` (5.3.5).
403#[derive(Debug, Clone, PartialEq, Default)]
404#[non_exhaustive]
405pub struct Representation {
406    /// `@id`.
407    pub id: String,
408    /// `@bandwidth`, bits per second.
409    pub bandwidth: u64,
410    /// `@qualityRanking`.
411    pub quality_ranking: Option<u32>,
412    /// `@dependencyId` (whitespace-separated Representation ids).
413    pub dependency_ids: Vec<String>,
414    /// Common attributes (not yet merged with the AdaptationSet's).
415    pub common: CommonAttributes,
416    /// `BaseURL` alternates.
417    pub base_urls: Vec<BaseUrl>,
418    /// Segment information declared here.
419    pub segment: SegmentLayer,
420    /// `ContentProtection`.
421    pub content_protections: Vec<Descriptor>,
422    /// `EssentialProperty`.
423    pub essential_properties: Vec<Descriptor>,
424    /// `SupplementalProperty`.
425    pub supplemental_properties: Vec<Descriptor>,
426    /// `AudioChannelConfiguration`.
427    pub audio_channel_configurations: Vec<Descriptor>,
428}
429
430/// An `AdaptationSet` (5.3.3).
431#[derive(Debug, Clone, PartialEq, Default)]
432#[non_exhaustive]
433pub struct AdaptationSet {
434    /// `@id`.
435    pub id: Option<u64>,
436    /// `@group`.
437    pub group: Option<u64>,
438    /// `@lang`.
439    pub lang: Option<String>,
440    /// `@contentType`.
441    pub content_type: Option<String>,
442    /// `@par`.
443    pub par: Option<String>,
444    /// `@segmentAlignment` (`true` or a non-zero count).
445    pub segment_alignment: bool,
446    /// `@startWithSAP`.
447    pub start_with_sap: Option<u8>,
448    /// `@bitstreamSwitching`.
449    pub bitstream_switching: Option<bool>,
450    /// Common attributes.
451    pub common: CommonAttributes,
452    /// `xlink` reference; the element is then unresolved.
453    pub xlink: Option<Xlink>,
454    /// `Role` descriptors.
455    pub roles: Vec<Descriptor>,
456    /// `Accessibility` descriptors.
457    pub accessibilities: Vec<Descriptor>,
458    /// `Label` texts.
459    pub labels: Vec<String>,
460    /// `BaseURL` alternates.
461    pub base_urls: Vec<BaseUrl>,
462    /// Segment information declared here.
463    pub segment: SegmentLayer,
464    /// `ContentProtection`.
465    pub content_protections: Vec<Descriptor>,
466    /// `EssentialProperty`.
467    pub essential_properties: Vec<Descriptor>,
468    /// `SupplementalProperty`.
469    pub supplemental_properties: Vec<Descriptor>,
470    /// `AudioChannelConfiguration`.
471    pub audio_channel_configurations: Vec<Descriptor>,
472    /// The Representations.
473    pub representations: Vec<Representation>,
474}
475
476/// A `Period` (5.3.2).
477#[derive(Debug, Clone, PartialEq, Default)]
478#[non_exhaustive]
479pub struct Period {
480    /// `@id`.
481    pub id: Option<String>,
482    /// `@start`.
483    pub start: Option<Duration>,
484    /// `@duration`.
485    pub duration: Option<Duration>,
486    /// `@bitstreamSwitching`.
487    pub bitstream_switching: bool,
488    /// `xlink` reference.
489    pub xlink: Option<Xlink>,
490    /// `BaseURL` alternates.
491    pub base_urls: Vec<BaseUrl>,
492    /// Segment information declared here.
493    pub segment: SegmentLayer,
494    /// `AssetIdentifier`.
495    pub asset_identifier: Option<Descriptor>,
496    /// `EssentialProperty`.
497    pub essential_properties: Vec<Descriptor>,
498    /// `SupplementalProperty`.
499    pub supplemental_properties: Vec<Descriptor>,
500    /// The AdaptationSets.
501    pub adaptation_sets: Vec<AdaptationSet>,
502}
503
504/// An `MPD` (5.3.1).
505#[derive(Debug, Clone, PartialEq, Default)]
506#[non_exhaustive]
507pub struct Mpd {
508    /// `@id`.
509    pub id: Option<String>,
510    /// `@profiles`, split on `,`.
511    pub profiles: Vec<String>,
512    /// `@type`.
513    pub kind: PresentationType,
514    /// `@availabilityStartTime`, Unix microseconds.
515    pub availability_start_time_us: Option<i64>,
516    /// `@availabilityEndTime`, Unix microseconds.
517    pub availability_end_time_us: Option<i64>,
518    /// `@publishTime`, Unix microseconds.
519    pub publish_time_us: Option<i64>,
520    /// `@mediaPresentationDuration`.
521    pub media_presentation_duration: Option<Duration>,
522    /// `@minimumUpdatePeriod`.
523    pub minimum_update_period: Option<Duration>,
524    /// `@minBufferTime`.
525    pub min_buffer_time: Option<Duration>,
526    /// `@timeShiftBufferDepth`.
527    pub time_shift_buffer_depth: Option<Duration>,
528    /// `@suggestedPresentationDelay`.
529    pub suggested_presentation_delay: Option<Duration>,
530    /// `@maxSegmentDuration`.
531    pub max_segment_duration: Option<Duration>,
532    /// `@maxSubsegmentDuration`.
533    pub max_subsegment_duration: Option<Duration>,
534    /// `BaseURL` alternates.
535    pub base_urls: Vec<BaseUrl>,
536    /// `Location` texts.
537    pub locations: Vec<String>,
538    /// `UTCTiming` elements in document order (alternatives, in preference
539    /// order).
540    pub utc_timings: Vec<UtcTiming>,
541    /// `EssentialProperty`.
542    pub essential_properties: Vec<Descriptor>,
543    /// `SupplementalProperty`.
544    pub supplemental_properties: Vec<Descriptor>,
545    /// The Periods.
546    pub periods: Vec<Period>,
547    /// Constructs seen but not acted on.
548    pub features: MpdFeatures,
549}