1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! Sans-IO MPEG-DASH: the MPD model and parser, segment addressing, the UTC
//! clock and the live-edge arithmetic a DASH client is built on, and
//! [`client::DashClient`], a client state machine for static (VOD) and
//! dynamic (live) presentations.
//!
//! **Spec:** ISO/IEC 23009-1:2022 (clause 5.3 MPD, 5.3.9 Segment information,
//! 5.6 BaseURL, 5.8.5.7 UTCTiming, Annex A client behaviour, Annex I URL
//! parameters), DASH-IF IOP v4.3 (live timing) and ETSI TS 103 285 (BaseURL
//! priority). The verified page references are in the private spec
//! repository (`private/specs-verified/dash/docs-specs/`); decisions where
//! the standard contradicts itself are in `docs/dash-runtime/DESIGN.md`
//! section 11.
//!
//! Nothing here does I/O or reads a clock. Every instant is an argument; the
//! caller fetches the MPD, the `sidx` and the segments, times its own
//! requests, and asks this crate what to fetch and when it becomes available.
//!
//! | Module | What it gives |
//! |---|---|
//! | [`mpd`] | [`mpd::Mpd`]: the consume-only model, a bounded namespace-aware parser, attribute inheritance down Period > AdaptationSet > Representation, BaseURL alternates and URL resolution |
//! | [`address`] | [`address::SegmentPlan`]: the lazy Segment list of a Representation (templates with width tags and `$$`, timelines with negative `@r` and `@n`, `SegmentList`, `SegmentBase` + `sidx`), with availability windows |
//! | [`timing`] | [`timing::clock::UtcClock`], `UTCTiming` sampling, `xs:dateTime` / HTTP `Date` / NTP readers, PeriodStart, availability and live-edge arithmetic |
//! | [`client`] | [`client::config::DashConfig`]: the DASH knobs of a client session (clock ladder, early-request cadence, refresh floor) |
//! | [`selection`] | Pure choice of AdaptationSet and Representation by role, language, codec and bandwidth |
//! | [`client`] | [`client::DashClient`]: plays static and dynamic MPDs to ordered `Fmp4Demux` samples, with typed failure classes, Period continuity and an origin pin |
//!
//! # Format policy
//!
//! The model is consume-only: there is no MPD serializer. The workspace
//! round-trip invariant is met against `transmux`'s DASH generator by a
//! differential test (`tests/differential.rs`): everything the generator
//! writes is parsed, and the two parsers agree on every field both read, the
//! expanded segment numbers and times, and the resolved URLs.
//!
//! # Scope of 0.1
//!
//! The client plays static and dynamic MPDs: a dynamic one gets a clock
//! (`UTCTiming`, the host clock via `DashClient::set_wall_clock`, or the MPD
//! response `Date`), a join near the live edge, availability gating and MPD
//! refresh.
//!
//! The core handles: static and dynamic MPDs, `SegmentTemplate` (`$Number$`, `$Time$`,
//! `$Bandwidth$`, `$RepresentationID$`), `SegmentTimeline`, `SegmentList`,
//! `SegmentBase` with a `sidx`, BaseURL alternates, multiple Periods,
//! `UTCTiming` (`direct`, `http-xsdate`, `http-iso`, `http-head`,
//! `http-ntp`). Typed `Unsupported` (the Representation is dropped, never
//! misread): segment sequences (`S@k`, `$SubNumber$`), `MPD@type="list"`,
//! `data:` addresses, open-ended byte ranges, hierarchical `sidx`.
//!
//! # Examples
//!
//! * `cargo run -p dash-runtime --example list_segments` prints the segment
//! URLs of an MPD file.
//! * `cargo run -p dash-runtime --example live_window` prints the live edge
//! of a dynamic MPD at a simulated clock.
//! * `cargo run -p dash-runtime --example play_live` plays a dynamic MPD
//! against a loopback origin and a simulated clock.
//! * `cargo run -p dash-runtime --example play_static` plays a static MPD from
//! the file system through `DashClient` and prints what a host receives.
//!
//! ```
//! use dash_runtime::address::{PlanOptions, plan_representation};
//! use dash_runtime::mpd::Mpd;
//! use dash_runtime::selection::RepRef;
//!
//! let xml = r#"<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" type="static"
//! mediaPresentationDuration="PT6S">
//! <Period><AdaptationSet mimeType="video/mp4">
//! <SegmentTemplate timescale="1000" duration="2000" startNumber="0"
//! media="$RepresentationID$/$Number%03d$.m4s"/>
//! <Representation id="v1" bandwidth="500000"/>
//! </AdaptationSet></Period>
//! </MPD>"#;
//! let mpd = Mpd::parse(xml.as_bytes()).unwrap();
//! let planned = plan_representation(&mpd, RepRef::at(0, 0, 0), &PlanOptions::default()).unwrap();
//! let refs: Vec<_> = planned.plan.iter().map(|s| s.reference).collect();
//! assert_eq!(refs, ["v1/000.m4s", "v1/001.m4s", "v1/002.m4s"]);
//! ```