Skip to main content

broadcast_dash/
descriptors.rs

1//! Descriptor scheme URIs and the typed readers over [`Descriptor`] lists.
2//!
3//! Role values: ISO/IEC 23009-1 5.8.5.5 Table 34. Continuity / connectivity /
4//! reset: 5.3.2.4, 5.4.2. URL parameters: Annex I.2. The last-segment-number
5//! property is DASH-IF IOP v4.3.
6
7use broadcast_common::impl_spec_display;
8
9use super::model::Descriptor;
10
11/// 5.8.5.5: the role scheme.
12pub const SCHEME_ROLE: &str = "urn:mpeg:dash:role:2011";
13/// 5.3.2.4: Period-continuity signalling.
14pub const SCHEME_PERIOD_CONTINUITY: &str = "urn:mpeg:dash:period-continuity:2015";
15/// 5.3.2.4: Period-connectivity signalling.
16pub const SCHEME_PERIOD_CONNECTIVITY: &str = "urn:mpeg:dash:period-connectivity:2015";
17/// 5.4.2: MPD reset.
18pub const SCHEME_RESET: &str = "urn:mpeg:dash:reset:2016";
19/// Annex I.2: URL parameter signalling (media Segment requests).
20pub const SCHEME_URLPARAM: &str = "urn:mpeg:dash:urlparam:2014";
21/// Annex I.3: the 2016 extended-parameterization scheme, a superset of
22/// [`SCHEME_URLPARAM`] that this crate does not understand.
23pub const SCHEME_URLPARAM_2016: &str = "urn:mpeg:dash:urlparam:2016";
24/// DASH-IF IOP v4.3: the last Segment number of a live Period.
25pub const SCHEME_LAST_SEGMENT_NUMBER: &str = "http://dashif.org/guidelines/last-segment-number";
26/// 5.5.3 rule 4: an xlink that removes its element.
27pub const XLINK_RESOLVE_TO_ZERO: &str = "urn:mpeg:dash:resolve-to-zero:2013";
28/// 5.8.5.2.2: the MP4 protection scheme (`ContentProtection`).
29pub const SCHEME_MP4_PROTECTION: &str = "urn:mpeg:dash:mp4protection:2011";
30
31/// A 5.8.5.5 role value.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33#[non_exhaustive]
34pub enum Role {
35    /// `caption`.
36    Caption,
37    /// `subtitle`.
38    Subtitle,
39    /// `main`.
40    Main,
41    /// `alternate`.
42    Alternate,
43    /// `supplementary`.
44    Supplementary,
45    /// `commentary`.
46    Commentary,
47    /// `dub`.
48    Dub,
49    /// `description`.
50    Description,
51    /// `sign`.
52    Sign,
53    /// `metadata`.
54    Metadata,
55    /// `enhanced-audio-intelligibility`.
56    EnhancedAudioIntelligibility,
57    /// `emergency`.
58    Emergency,
59    /// `forced-subtitle`.
60    ForcedSubtitle,
61    /// `easyreader`.
62    EasyReader,
63    /// `karaoke`.
64    Karaoke,
65    /// Any other token.
66    Other,
67}
68
69impl Role {
70    /// Classify a `Role@value` of the DASH role scheme.
71    #[must_use]
72    pub fn from_value(value: &str) -> Self {
73        match value {
74            "caption" => Self::Caption,
75            "subtitle" => Self::Subtitle,
76            "main" => Self::Main,
77            "alternate" => Self::Alternate,
78            "supplementary" => Self::Supplementary,
79            "commentary" => Self::Commentary,
80            "dub" => Self::Dub,
81            "description" => Self::Description,
82            "sign" => Self::Sign,
83            "metadata" => Self::Metadata,
84            "enhanced-audio-intelligibility" => Self::EnhancedAudioIntelligibility,
85            "emergency" => Self::Emergency,
86            "forced-subtitle" => Self::ForcedSubtitle,
87            "easyreader" => Self::EasyReader,
88            "karaoke" => Self::Karaoke,
89            _ => Self::Other,
90        }
91    }
92
93    /// The Table 34 token, or `"other"`.
94    #[must_use]
95    pub fn name(&self) -> &'static str {
96        match self {
97            Self::Caption => "caption",
98            Self::Subtitle => "subtitle",
99            Self::Main => "main",
100            Self::Alternate => "alternate",
101            Self::Supplementary => "supplementary",
102            Self::Commentary => "commentary",
103            Self::Dub => "dub",
104            Self::Description => "description",
105            Self::Sign => "sign",
106            Self::Metadata => "metadata",
107            Self::EnhancedAudioIntelligibility => "enhanced-audio-intelligibility",
108            Self::Emergency => "emergency",
109            Self::ForcedSubtitle => "forced-subtitle",
110            Self::EasyReader => "easyreader",
111            Self::Karaoke => "karaoke",
112            Self::Other => "other",
113        }
114    }
115}
116impl_spec_display!(Role);
117
118/// The first descriptor with `scheme`.
119#[must_use]
120pub fn find<'a>(descriptors: &'a [Descriptor], scheme: &str) -> Option<&'a Descriptor> {
121    descriptors.iter().find(|d| d.scheme_id_uri == scheme)
122}
123
124/// Schemes an `EssentialProperty` may name without making the parent element
125/// unprocessable (5.8.4.8: a client that does not understand an essential
126/// scheme drops the parent).
127pub const UNDERSTOOD_ESSENTIAL: &[&str] = &[
128    SCHEME_PERIOD_CONTINUITY,
129    SCHEME_PERIOD_CONNECTIVITY,
130    SCHEME_RESET,
131    SCHEME_URLPARAM,
132    SCHEME_LAST_SEGMENT_NUMBER,
133];
134
135/// Whether this core understands an `EssentialProperty` scheme.
136#[must_use]
137pub fn essential_understood(descriptor: &Descriptor) -> bool {
138    UNDERSTOOD_ESSENTIAL.contains(&descriptor.scheme_id_uri.as_str())
139}
140
141#[cfg(test)]
142mod scheme_tests {
143    use super::*;
144
145    #[test]
146    fn scheme_constants_are_the_registered_uris() {
147        assert_eq!(SCHEME_URLPARAM_2016, "urn:mpeg:dash:urlparam:2016");
148        assert_eq!(SCHEME_MP4_PROTECTION, "urn:mpeg:dash:mp4protection:2011");
149        assert_ne!(SCHEME_URLPARAM_2016, SCHEME_URLPARAM);
150    }
151
152    #[test]
153    fn the_2016_urlparam_scheme_is_not_an_understood_essential_property() {
154        let d = Descriptor {
155            scheme_id_uri: SCHEME_URLPARAM_2016.into(),
156            ..Descriptor::default()
157        };
158        assert!(!essential_understood(&d));
159        let known = Descriptor {
160            scheme_id_uri: SCHEME_URLPARAM.into(),
161            ..Descriptor::default()
162        };
163        assert!(essential_understood(&known));
164    }
165}