1#![allow(non_snake_case)]
65
66#[cfg(feature = "fetch")]
71pub mod media;
72#[cfg(all(feature = "fetch", feature = "libav"))]
73mod libav;
74#[cfg(all(feature = "fetch", not(feature = "libav")))]
75pub mod ffmpeg;
76#[cfg(feature = "fetch")]
77pub mod sidx;
78#[cfg(feature = "fetch")]
79pub mod fetch;
80#[cfg(feature = "fetch")]
81pub mod decryption;
82#[cfg(feature = "scte35")]
84pub mod scte35;
85#[cfg(feature = "scte35")]
86use crate::scte35::{Signal, SpliceInfoSection};
87
88#[cfg(all(feature = "fetch", feature = "libav"))]
89use crate::libav::{mux_audio_video, copy_video_to_container, copy_audio_to_container};
90#[cfg(all(feature = "fetch", not(feature = "libav")))]
91use crate::ffmpeg::{mux_audio_video, copy_video_to_container, copy_audio_to_container};
92
93#[cfg(all(feature = "sandbox", feature = "fetch", target_os = "linux"))]
94pub mod sandbox;
95
96use serde::{Serialize, Serializer, Deserialize};
97use serde::de;
98use serde_with::skip_serializing_none;
99use regex::Regex;
100use std::sync::LazyLock;
101use std::time::Duration;
102use chrono::DateTime;
103use url::Url;
104#[allow(unused_imports)]
105use tracing::warn;
106
107static XS_DURATION_REGEX: LazyLock<Regex> = LazyLock::new(||
109 Regex::new(concat!(r"^(?P<sign>[+-])?P",
110 r"(?:(?P<years>\d+)Y)?",
111 r"(?:(?P<months>\d+)M)?",
112 r"(?:(?P<weeks>\d+)W)?",
113 r"(?:(?P<days>\d+)D)?",
114 r"(?:(?P<hastime>T)", r"(?:(?P<hours>\d+)H)?",
116 r"(?:(?P<minutes>\d+)M)?",
117 r"(?:(?P<seconds>\d+)(?:(?P<nanoseconds>[.,]\d+)?)S)?",
118 r")?")).unwrap()
119);
120
121pub type XsDatetime = DateTime<chrono::offset::Utc>;
124
125#[derive(thiserror::Error, Debug)]
126#[non_exhaustive]
127pub enum DashMpdError {
128 #[error("parse error {0:?}")]
129 Parsing(String),
130 #[error("invalid Duration: {0:?}")]
131 InvalidDuration(String),
132 #[error("invalid DateTime: {0:?}")]
133 InvalidDateTime(String),
134 #[error("invalid media stream: {0:?}")]
135 UnhandledMediaStream(String),
136 #[error("I/O error {1} ({0:?})")]
137 Io(#[source] std::io::Error, String),
138 #[error("network error {0:?}")]
139 Network(String),
140 #[error("network timeout: {0:?}")]
141 NetworkTimeout(String),
142 #[error("network connection: {0:?}")]
143 NetworkConnect(String),
144 #[error("muxing error {0:?}")]
145 Muxing(String),
146 #[error("decryption error {0:?}")]
147 Decrypting(String),
148 #[error("{0:?}")]
149 Other(String),
150}
151
152
153fn serialize_xsd_double<S>(xsd: &f64, serializer: S) -> Result<S::Ok, S::Error>
158where
159 S: Serializer,
160{
161 let formatted = if xsd.is_nan() {
162 String::from("NaN")
163 } else if xsd.is_infinite() {
164 if xsd.is_sign_positive() {
165 String::from("INF")
167 } else {
168 String::from("-INF")
169 }
170 } else {
171 xsd.to_string()
172 };
173 serializer.serialize_str(&formatted)
174}
175
176fn serialize_opt_xsd_double<S>(oxsd: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
178where
179 S: Serializer,
180{
181 if let Some(xsd) = oxsd {
182 serialize_xsd_double(xsd, serializer)
183 } else {
184 serializer.serialize_none()
186 }
187}
188
189
190fn parse_xs_duration(s: &str) -> Result<Duration, DashMpdError> {
206 use std::cmp::min;
207
208 match XS_DURATION_REGEX.captures(s) {
209 Some(m) => {
210 if m.name("hastime").is_none() &&
211 m.name("years").is_none() &&
212 m.name("months").is_none() &&
213 m.name("weeks").is_none() &&
214 m.name("days").is_none() {
215 return Err(DashMpdError::InvalidDuration("empty".to_string()));
216 }
217 let mut secs: u64 = 0;
218 let mut nsecs: u32 = 0;
219 if let Some(nano) = m.name("nanoseconds") {
220 let lim = min(nano.as_str().len(), 9 + ".".len());
223 if let Some(ss) = &nano.as_str().get(1..lim) {
224 let padded = format!("{ss:0<9}");
225 nsecs = padded.parse::<u32>()
226 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
227 }
228 }
229 if let Some(mseconds) = m.name("seconds") {
230 let seconds = mseconds.as_str().parse::<u64>()
231 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
232 secs += seconds;
233 }
234 if let Some(mminutes) = m.name("minutes") {
235 let minutes = mminutes.as_str().parse::<u64>()
236 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
237 secs += minutes * 60;
238 }
239 if let Some(mhours) = m.name("hours") {
240 let hours = mhours.as_str().parse::<u64>()
241 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
242 secs += hours * 60 * 60;
243 }
244 if let Some(mdays) = m.name("days") {
245 let days = mdays.as_str().parse::<u64>()
246 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
247 secs += days * 60 * 60 * 24;
248 }
249 if let Some(mweeks) = m.name("weeks") {
250 let weeks = mweeks.as_str().parse::<u64>()
251 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
252 secs += weeks * 60 * 60 * 24 * 7;
253 }
254 if let Some(mmonths) = m.name("months") {
255 let months = mmonths.as_str().parse::<u64>()
256 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
257 secs += months * 60 * 60 * 24 * 30;
258 }
259 if let Some(myears) = m.name("years") {
260 let years = myears.as_str().parse::<u64>()
261 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
262 secs += years * 60 * 60 * 24 * 365;
263 }
264 if let Some(msign) = m.name("sign") {
265 if msign.as_str() == "-" {
266 return Err(DashMpdError::InvalidDuration("can't represent negative durations".to_string()));
267 }
268 }
269 Ok(Duration::new(secs, nsecs))
270 },
271 None => Err(DashMpdError::InvalidDuration(String::from("couldn't parse XS duration"))),
272 }
273}
274
275
276fn deserialize_xs_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
324where
325 D: de::Deserializer<'de>,
326{
327 match <Option<String>>::deserialize(deserializer) {
328 Ok(optstring) => match optstring {
329 Some(xs) => match parse_xs_duration(&xs) {
330 Ok(d) => Ok(Some(d)),
331 Err(e) => Err(de::Error::custom(e)),
332 },
333 None => Ok(None),
334 },
335 Err(_) => Ok(None),
337 }
338}
339
340fn serialize_xs_duration<S>(oxs: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
348where
349 S: Serializer,
350{
351 if let Some(xs) = oxs {
352 let seconds = xs.as_secs();
353 let nanos = xs.subsec_nanos();
354 let minutes = seconds / 60;
355 let hours = if minutes > 59 { minutes / 60 } else { 0 };
356 let fractional_maybe = if nanos > 0 {
357 format!(".{nanos:09}").trim_end_matches('0').to_string()
358 } else {
359 "".to_string()
360 };
361 let formatted_duration = if hours > 0 {
362 let mins = minutes % 60;
363 let secs = seconds % 60;
364 format!("PT{hours}H{mins}M{secs}{fractional_maybe}S")
365 } else if minutes > 0 {
366 let secs = seconds % 60;
367 format!("PT{minutes}M{secs}{fractional_maybe}S")
368 } else {
369 format!("PT{seconds}{fractional_maybe}S")
370 };
371 serializer.serialize_str(&formatted_duration)
372 } else {
373 serializer.serialize_none()
375 }
376}
377
378
379fn parse_xs_datetime(s: &str) -> Result<XsDatetime, DashMpdError> {
385 use iso8601::Date;
386 use chrono::{LocalResult, NaiveDate, TimeZone};
387 use num_traits::cast::FromPrimitive;
388 match DateTime::<chrono::offset::FixedOffset>::parse_from_rfc3339(s) {
389 Ok(dt) => Ok(dt.into()),
390 Err(_) => match iso8601::datetime(s) {
391 Ok(dt) => {
392 let nd = match dt.date {
393 Date::YMD { year, month, day } =>
394 NaiveDate::from_ymd_opt(year, month, day)
395 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
396 Date::Week { year, ww, d } => {
397 let d = chrono::Weekday::from_u32(d)
398 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
399 NaiveDate::from_isoywd_opt(year, ww, d)
400 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
401 },
402 Date::Ordinal { year, ddd } =>
403 NaiveDate::from_yo_opt(year, ddd)
404 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
405 };
406 let nd = nd.and_hms_nano_opt(dt.time.hour, dt.time.minute, dt.time.second, dt.time.millisecond*1000*1000)
407 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
408 let tz_secs = dt.time.tz_offset_hours * 3600 + dt.time.tz_offset_minutes * 60;
409 match chrono::FixedOffset::east_opt(tz_secs)
410 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
411 .from_local_datetime(&nd)
412 {
413 LocalResult::Single(local) => Ok(local.with_timezone(&chrono::Utc)),
414 _ => Err(DashMpdError::InvalidDateTime(s.to_string())),
415 }
416 },
417 Err(_) => Err(DashMpdError::InvalidDateTime(s.to_string())),
418 }
419 }
420}
421
422fn deserialize_xs_datetime<'de, D>(deserializer: D) -> Result<Option<XsDatetime>, D::Error>
424where
425 D: de::Deserializer<'de>,
426{
427 match <Option<String>>::deserialize(deserializer) {
428 Ok(optstring) => match optstring {
429 Some(xs) => match parse_xs_datetime(&xs) {
430 Ok(d) => Ok(Some(d)),
431 Err(e) => Err(de::Error::custom(e)),
432 },
433 None => Ok(None),
434 },
435 Err(_) => Ok(None),
437 }
438}
439
440fn serialize_xsd_uintvector<S>(v: &Vec<u64>, serializer: S) -> Result<S::Ok, S::Error>
443where
444 S: Serializer,
445{
446 let mut formatted = String::new();
447 for u in v {
448 formatted += &format!("{u} ");
449 }
450 serializer.serialize_str(&formatted)
451}
452
453fn deserialize_xsd_uintvector<'de, D>(deserializer: D) -> Result<Vec<u64>, D::Error>
454where
455 D: de::Deserializer<'de>,
456{
457 let s = String::deserialize(deserializer)?;
458 let mut out = Vec::<u64>::new();
459 for uint64_str in s.split_whitespace() {
460 match uint64_str.parse::<u64>() {
461 Ok(val) => out.push(val),
462 Err(e) => return Err(de::Error::custom(e)),
463 }
464 }
465 Ok(out)
466}
467
468fn serialize_xmlns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
478where S: serde::Serializer {
479 if let Some(s) = os {
480 serializer.serialize_str(s)
481 } else {
482 serializer.serialize_str("urn:mpeg:dash:schema:mpd:2011")
483 }
484}
485
486fn serialize_xsi_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
487where S: serde::Serializer {
488 if let Some(s) = os {
489 serializer.serialize_str(s)
490 } else {
491 serializer.serialize_str("http://www.w3.org/2001/XMLSchema-instance")
492 }
493}
494
495fn serialize_cenc_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
496where S: serde::Serializer {
497 if let Some(s) = os {
498 serializer.serialize_str(s)
499 } else {
500 serializer.serialize_str("urn:mpeg:cenc:2013")
501 }
502}
503
504fn serialize_mspr_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
505where S: serde::Serializer {
506 if let Some(s) = os {
507 serializer.serialize_str(s)
508 } else {
509 serializer.serialize_str("urn:microsoft:playready")
510 }
511}
512
513fn serialize_xlink_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
514where S: serde::Serializer {
515 if let Some(s) = os {
516 serializer.serialize_str(s)
517 } else {
518 serializer.serialize_str("http://www.w3.org/1999/xlink")
519 }
520}
521
522fn serialize_dvb_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
523where S: serde::Serializer {
524 if let Some(s) = os {
525 serializer.serialize_str(s)
526 } else {
527 serializer.serialize_str("urn:dvb:dash-extensions:2014-1")
528 }
529}
530
531
532fn default_optstring_on_request() -> Option<String> {
536 Some("onRequest".to_string())
537}
538
539fn default_optstring_one() -> Option<String> {
540 Some(String::from("1"))
541}
542
543fn default_optstring_encoder() -> Option<String> {
544 Some(String::from("encoder"))
545}
546
547fn default_optstring_any() -> Option<String> {
548 Some(String::from("any"))
549}
550
551fn default_optstring_query() -> Option<String> {
552 Some(String::from("query"))
553}
554
555fn default_optstring_segment() -> Option<String> {
556 Some(String::from("segment"))
557}
558
559fn default_optbool_true() -> Option<bool> {
560 Some(true)
561}
562
563fn default_optbool_false() -> Option<bool> {
564 Some(false)
565}
566
567fn default_optu64_zero() -> Option<u64> {
568 Some(0)
569}
570
571fn default_optu64_one() -> Option<u64> {
572 Some(1)
573}
574
575
576#[skip_serializing_none]
589#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
590#[serde(default)]
591pub struct Title {
592 #[serde(rename = "$text")]
593 pub content: Option<String>,
594}
595
596#[skip_serializing_none]
598#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
599#[serde(default)]
600pub struct Source {
601 #[serde(rename = "$text")]
602 pub content: Option<String>,
603}
604
605#[skip_serializing_none]
607#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
608#[serde(default)]
609pub struct Copyright {
610 #[serde(rename = "$text")]
611 pub content: Option<String>,
612}
613
614#[skip_serializing_none]
616#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
617#[serde(default)]
618pub struct ProgramInformation {
619 #[serde(rename = "@lang")]
621 pub lang: Option<String>,
622 #[serde(rename = "@moreInformationURL")]
623 pub moreInformationURL: Option<String>,
624 pub Title: Option<Title>,
625 pub Source: Option<Source>,
626 pub Copyright: Option<Copyright>,
627 #[serde(rename(serialize = "scte214:ContentIdentifier", deserialize = "ContentIdentifier"))]
628 pub scte214ContentIdentifier: Option<Scte214ContentIdentifier>,
629}
630
631#[skip_serializing_none]
635#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
636#[serde(default)]
637pub struct Scte214ContentIdentifier {
638 #[serde(rename = "@type")]
639 pub idType: Option<String>,
640 #[serde(rename = "@value")]
641 pub idValue: Option<String>,
642}
643
644#[skip_serializing_none]
646#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
647#[serde(default)]
648pub struct S {
649 #[serde(rename = "@t")]
651 pub t: Option<u64>,
652 #[serde(rename = "@n")]
653 pub n: Option<u64>,
654 #[serde(rename = "@d")]
656 pub d: u64,
657 #[serde(rename = "@r")]
660 pub r: Option<i64>,
661 #[serde(rename = "@k")]
662 pub k: Option<u64>,
663}
664
665#[skip_serializing_none]
668#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
669#[serde(default)]
670pub struct SegmentTimeline {
671 #[serde(rename = "S")]
673 pub segments: Vec<S>,
674}
675
676#[skip_serializing_none]
683#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
684#[serde(default)]
685pub struct BitstreamSwitching {
686 #[serde(rename = "@sourceURL")]
687 pub source_url: Option<String>,
688 #[serde(rename = "@range")]
689 pub range: Option<String>,
690}
691
692#[skip_serializing_none]
696#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
697#[serde(default)]
698pub struct Initialization {
699 #[serde(rename = "@sourceURL")]
700 pub sourceURL: Option<String>,
701 #[serde(rename = "@range")]
702 pub range: Option<String>,
703}
704
705#[skip_serializing_none]
706#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
707#[serde(default)]
708pub struct RepresentationIndex {
709 #[serde(rename = "@range")]
710 pub range: Option<String>,
711 #[serde(rename = "@sourceURL")]
712 pub sourceURL: Option<String>,
713}
714
715#[skip_serializing_none]
718#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
719#[serde(default)]
720pub struct SegmentTemplate {
721 #[serde(rename = "@media")]
722 pub media: Option<String>,
723 #[serde(rename = "@index")]
724 pub index: Option<String>,
725 #[serde(rename = "@initialization")]
726 pub initialization: Option<String>,
727 #[serde(rename = "@bitstreamSwitching")]
728 pub bitstreamSwitching: Option<String>,
729 #[serde(rename = "@indexRange")]
730 pub indexRange: Option<String>,
731 #[serde(rename = "@indexRangeExact")]
732 pub indexRangeExact: Option<bool>,
733 #[serde(rename = "@startNumber")]
734 pub startNumber: Option<u64>,
735 #[serde(rename = "@duration")]
739 pub duration: Option<f64>,
740 #[serde(rename = "@timescale")]
741 pub timescale: Option<u64>,
742 #[serde(rename = "@eptDelta")]
744 pub eptDelta: Option<i64>,
745 #[serde(rename = "@pdDelta")]
748 pub pbDelta: Option<i64>,
749 #[serde(rename = "@presentationTimeOffset")]
750 pub presentationTimeOffset: Option<u64>,
751 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
752 pub availabilityTimeOffset: Option<f64>,
753 #[serde(rename = "@availabilityTimeComplete")]
754 pub availabilityTimeComplete: Option<bool>,
755 pub Initialization: Option<Initialization>,
756 #[serde(rename = "RepresentationIndex")]
757 pub representation_index: Option<RepresentationIndex>,
758 #[serde(rename = "FailoverContent")]
762 pub failover_content: Option<FailoverContent>,
763 pub SegmentTimeline: Option<SegmentTimeline>,
764 pub BitstreamSwitching: Option<BitstreamSwitching>,
765}
766
767#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
771#[serde(default)]
772pub struct Location {
773 #[serde(rename = "$text")]
774 pub url: String,
775}
776
777#[skip_serializing_none]
783#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
784#[serde(default)]
785pub struct BaseURL {
786 #[serde(rename = "@serviceLocation")]
787 pub serviceLocation: Option<String>,
788 #[serde(rename = "@byteRange")]
789 pub byte_range: Option<String>,
790 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
793 pub availability_time_offset: Option<f64>,
794 #[serde(rename = "@availabilityTimeComplete")]
795 pub availability_time_complete: Option<bool>,
796 #[serde(rename = "@timeShiftBufferDepth",
797 serialize_with = "serialize_xs_duration",
798 deserialize_with = "deserialize_xs_duration",
799 default)]
800 pub timeShiftBufferDepth: Option<Duration>,
801 #[serde(rename = "@dvb:priority", alias = "@priority")]
803 pub priority: Option<u64>,
804 #[serde(rename = "@dvb:weight", alias = "@weight")]
808 pub weight: Option<i64>,
809 #[serde(rename = "$text")]
810 pub base: String,
811}
812
813#[skip_serializing_none]
819#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
820#[serde(default)]
821pub struct Fcs {
822 #[serde(rename = "@t")]
825 pub t: u64,
826
827 #[serde(rename = "@d")]
830 pub d: Option<u64>,
831}
832
833#[skip_serializing_none]
836#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
837#[serde(default)]
838pub struct FailoverContent {
839 #[serde(rename = "@valid")]
842 pub valid: Option<bool>,
843 #[serde(rename = "FCS")]
844 pub fcs_list: Vec<Fcs>,
845}
846
847#[skip_serializing_none]
849#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
850#[serde(default)]
851pub struct SegmentBase {
852 #[serde(rename = "@timescale")]
853 pub timescale: Option<u64>,
854 #[serde(rename = "@presentationTimeOffset")]
855 pub presentationTimeOffset: Option<u64>,
856 #[serde(rename = "@indexRange")]
857 pub indexRange: Option<String>,
858 #[serde(rename = "@indexRangeExact")]
859 pub indexRangeExact: Option<bool>,
860 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
861 pub availabilityTimeOffset: Option<f64>,
862 #[serde(rename = "@availabilityTimeComplete")]
863 pub availabilityTimeComplete: Option<bool>,
864 #[serde(rename = "@presentationDuration")]
865 pub presentationDuration: Option<u64>,
866 #[serde(rename = "@eptDelta")]
868 pub eptDelta: Option<i64>,
869 #[serde(rename = "@pdDelta")]
872 pub pbDelta: Option<i64>,
873 pub Initialization: Option<Initialization>,
874 #[serde(rename = "RepresentationIndex")]
875 pub representation_index: Option<RepresentationIndex>,
876 #[serde(rename = "FailoverContent")]
877 pub failover_content: Option<FailoverContent>,
878}
879
880#[skip_serializing_none]
882#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
883#[serde(default)]
884pub struct SegmentURL {
885 #[serde(rename = "@media")]
886 pub media: Option<String>, #[serde(rename = "@mediaRange")]
888 pub mediaRange: Option<String>,
889 #[serde(rename = "@index")]
890 pub index: Option<String>, #[serde(rename = "@indexRange")]
892 pub indexRange: Option<String>,
893}
894
895#[skip_serializing_none]
897#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
898#[serde(default)]
899pub struct SegmentList {
900 #[serde(rename = "@duration")]
902 pub duration: Option<u64>,
903 #[serde(rename = "@timescale")]
904 pub timescale: Option<u64>,
905 #[serde(rename = "@indexRange")]
906 pub indexRange: Option<String>,
907 #[serde(rename = "@indexRangeExact")]
908 pub indexRangeExact: Option<bool>,
909 #[serde(rename = "@xlink:href", alias = "@href")]
911 pub href: Option<String>,
912 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
913 pub actuate: Option<String>,
914 #[serde(rename = "@xlink:type", alias = "@type")]
915 pub sltype: Option<String>,
916 #[serde(rename = "@xlink:show", alias = "@show")]
917 pub show: Option<String>,
918 pub Initialization: Option<Initialization>,
919 pub SegmentTimeline: Option<SegmentTimeline>,
920 pub BitstreamSwitching: Option<BitstreamSwitching>,
921 #[serde(rename = "SegmentURL")]
922 pub segment_urls: Vec<SegmentURL>,
923}
924
925#[skip_serializing_none]
926#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
927#[serde(default)]
928pub struct Resync {
929 #[serde(rename = "@type")]
930 pub rtype: Option<String>,
931 #[serde(rename = "@dT")]
932 pub dT: Option<u64>,
933 #[serde(rename = "@dImax")]
934 pub dImax: Option<f64>,
935 #[serde(rename = "@dImin")]
936 pub dImin: Option<f64>,
937 #[serde(rename = "@marker")]
938 pub marker: Option<bool>,
939}
940
941#[skip_serializing_none]
943#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
944#[serde(default)]
945pub struct AudioChannelConfiguration {
946 #[serde(rename = "@schemeIdUri")]
947 pub schemeIdUri: String,
948 #[serde(rename = "@value")]
949 pub value: Option<String>,
950 #[serde(rename = "@id")]
951 pub id: Option<String>,
952}
953
954#[skip_serializing_none]
956#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
957#[serde(default)]
958pub struct Language {
959 #[serde(rename = "$text")]
960 pub content: Option<String>,
961}
962
963#[skip_serializing_none]
969#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
970#[serde(default)]
971pub struct Preselection {
972 #[serde(rename = "@id", default = "default_optstring_one")]
973 pub id: Option<String>,
974 #[serde(rename = "@preselectionComponents")]
977 pub preselectionComponents: String,
978 #[serde(rename = "@lang")]
979 pub lang: Option<String>,
980 #[serde(rename = "@audioSamplingRate")]
981 pub audioSamplingRate: Option<String>,
982 #[serde(rename = "@codecs")]
984 pub codecs: String,
985 #[serde(rename = "@selectionPriority")]
986 pub selectionPriority: Option<u64>,
987 #[serde(rename = "@tag")]
988 pub tag: String,
989 pub FramePacking: Vec<FramePacking>,
990 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
991 pub ContentProtection: Vec<ContentProtection>,
992 pub OutputProtection: Option<OutputProtection>,
993 #[serde(rename = "EssentialProperty")]
994 pub essential_property: Vec<EssentialProperty>,
995 #[serde(rename = "SupplementalProperty")]
996 pub supplemental_property: Vec<SupplementalProperty>,
997 pub InbandEventStream: Vec<InbandEventStream>,
998 pub Switching: Vec<Switching>,
999 #[serde(rename = "GroupLabel")]
1001 pub group_label: Vec<Label>,
1002 pub Label: Vec<Label>,
1003 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
1004 pub Resync: Option<Resync>,
1006 #[serde(rename = "Accessibility")]
1007 pub accessibilities: Vec<Accessibility>,
1008 #[serde(rename = "Role")]
1009 pub roles: Vec<Role>,
1010 #[serde(rename = "Rating")]
1011 pub ratings: Vec<Rating>,
1012 #[serde(rename = "Viewpoint")]
1013 pub viewpoints: Vec<Viewpoint>,
1014 #[serde(rename = "Language")]
1016 pub languages: Vec<Language>,
1017}
1018
1019#[skip_serializing_none]
1022#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1023#[serde(default)]
1024pub struct Rating {
1025 #[serde(rename = "@id")]
1026 pub id: Option<String>,
1027 #[serde(rename = "@schemeIdUri")]
1028 pub schemeIdUri: String,
1029 #[serde(rename = "@value")]
1030 pub value: Option<String>,
1031}
1032
1033#[skip_serializing_none]
1035#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1036#[serde(default)]
1037pub struct FramePacking {
1038 #[serde(rename = "@id")]
1039 pub id: Option<String>,
1040 #[serde(rename = "@schemeIdUri")]
1041 pub schemeIdUri: String,
1042 #[serde(rename = "@value")]
1043 pub value: Option<String>,
1044}
1045
1046#[skip_serializing_none]
1051#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1052#[serde(default)]
1053pub struct Switching {
1054 #[serde(rename = "@interval")]
1055 pub interval: Option<u64>,
1056 #[serde(rename = "@type")]
1058 pub stype: Option<String>,
1059}
1060
1061#[skip_serializing_none]
1063#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1064#[serde(default)]
1065pub struct Accessibility {
1066 #[serde(rename = "@schemeIdUri")]
1067 pub schemeIdUri: String,
1068 #[serde(rename = "@value")]
1069 pub value: Option<String>,
1070 #[serde(rename = "@id")]
1071 pub id: Option<String>,
1072}
1073
1074#[skip_serializing_none]
1076#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1077#[serde(default)]
1078pub struct Scope {
1079 #[serde(rename = "@schemeIdUri")]
1080 pub schemeIdUri: String,
1081 #[serde(rename = "@value")]
1082 pub value: Option<String>,
1083 #[serde(rename = "@id")]
1084 pub id: Option<String>,
1085}
1086
1087#[skip_serializing_none]
1089#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1090#[serde(default)]
1091pub struct SubRepresentation {
1092 #[serde(rename = "@level")]
1093 pub level: Option<u32>,
1094 #[serde(rename = "@dependencyLevel")]
1095 pub dependencyLevel: Option<String>,
1096 #[serde(rename = "@contentComponent")]
1098 pub contentComponent: Option<String>,
1099 #[serde(rename = "@mimeType")]
1100 pub mimeType: Option<String>,
1101 #[serde(rename = "@codecs")]
1103 pub codecs: Option<String>,
1104 #[serde(rename = "@contentType")]
1105 pub contentType: Option<String>,
1106 #[serde(rename = "@profiles")]
1107 pub profiles: Option<String>,
1108 #[serde(rename = "@segmentProfiles")]
1109 pub segmentProfiles: Option<String>,
1112 #[serde(rename = "@scanType")]
1114 pub scanType: Option<String>,
1115 #[serde(rename = "@frameRate")]
1116 pub frameRate: Option<String>, #[serde(rename = "@sar")]
1119 pub sar: Option<String>,
1120 #[serde(rename = "@bandwidth")]
1122 pub bandwidth: Option<u64>,
1123 #[serde(rename = "@audioSamplingRate")]
1124 pub audioSamplingRate: Option<String>,
1125 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1127 pub maxPlayoutRate: Option<f64>,
1128 #[serde(rename = "@codingDependency")]
1129 pub codingDependency: Option<bool>,
1130 #[serde(rename = "@width")]
1131 pub width: Option<u64>,
1132 #[serde(rename = "@height")]
1133 pub height: Option<u64>,
1134 #[serde(rename = "@startWithSAP")]
1135 pub startWithSAP: Option<u64>,
1136 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1137 pub maximumSAPPeriod: Option<f64>,
1138 pub FramePacking: Vec<FramePacking>,
1139 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1140 pub ContentProtection: Vec<ContentProtection>,
1141 pub OutputProtection: Option<OutputProtection>,
1142 #[serde(rename = "EssentialProperty")]
1143 pub essential_property: Vec<EssentialProperty>,
1144 #[serde(rename = "SupplementalProperty")]
1145 pub supplemental_property: Vec<SupplementalProperty>,
1146 pub InbandEventStream: Vec<InbandEventStream>,
1147 pub Switching: Vec<Switching>,
1148 #[serde(rename = "GroupLabel")]
1150 pub group_label: Vec<Label>,
1151 pub Label: Vec<Label>,
1152 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
1153 pub Resync: Option<Resync>,
1155}
1156
1157#[skip_serializing_none]
1162#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1163#[serde(default)]
1164pub struct Representation {
1165 #[serde(rename = "@id")]
1167 pub id: Option<String>,
1168 #[serde(rename = "@bandwidth")]
1170 pub bandwidth: Option<u64>,
1171 #[serde(rename = "@qualityRanking")]
1175 pub qualityRanking: Option<u8>,
1176 #[serde(rename = "@dependencyId")]
1180 pub dependencyId: Option<String>,
1181 #[serde(rename = "@associationId")]
1182 pub associationId: Option<String>,
1183 #[serde(rename = "@associationType")]
1184 pub associationType: Option<String>,
1185 #[serde(rename = "@mediaStreamStructureId")]
1186 pub mediaStreamStructureId: Option<String>,
1187 #[serde(rename = "@profiles")]
1188 pub profiles: Option<String>,
1189 #[serde(rename = "@width")]
1190 pub width: Option<u64>,
1191 #[serde(rename = "@height")]
1192 pub height: Option<u64>,
1193 #[serde(rename = "@sar")]
1195 pub sar: Option<String>,
1196 #[serde(rename = "@frameRate")]
1197 pub frameRate: Option<String>, #[serde(rename = "@audioSamplingRate")]
1199 pub audioSamplingRate: Option<String>,
1200 #[serde(rename = "@mimeType")]
1203 pub mimeType: Option<String>,
1204 #[serde(rename = "@segmentProfiles")]
1207 pub segmentProfiles: Option<String>,
1208 #[serde(rename = "@codecs")]
1211 pub codecs: Option<String>,
1212 #[serde(rename = "@containerProfiles")]
1213 pub containerProfiles: Option<String>,
1214 #[serde(rename = "@maximumSAPPeriod")]
1215 pub maximumSAPPeriod: Option<f64>,
1216 #[serde(rename = "@startWithSAP")]
1217 pub startWithSAP: Option<u64>,
1218 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1220 pub maxPlayoutRate: Option<f64>,
1221 #[serde(rename = "@codingDependency")]
1222 pub codingDependency: Option<bool>,
1223 #[serde(rename = "@scanType")]
1225 pub scanType: Option<String>,
1226 #[serde(rename = "@selectionPriority")]
1227 pub selectionPriority: Option<u64>,
1228 #[serde(rename = "@tag")]
1229 pub tag: Option<String>,
1230 #[serde(rename = "@contentType")]
1231 pub contentType: Option<String>,
1232 #[serde(rename = "@lang")]
1234 pub lang: Option<String>,
1235 #[serde(rename = "@sampleRate")]
1236 pub sampleRate: Option<u64>,
1237 #[serde(rename = "@numChannels")]
1238 pub numChannels: Option<u32>,
1239 #[serde(rename = "@xlink:href", alias = "@href")]
1240 pub href: Option<String>,
1241 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1242 pub actuate: Option<String>,
1243 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1244 pub scte214_supplemental_profiles: Option<String>,
1245 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1246 pub scte214_supplemental_codecs: Option<String>,
1247 pub FramePacking: Vec<FramePacking>,
1248 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1249 pub ContentProtection: Vec<ContentProtection>,
1250 pub OutputProtection: Option<OutputProtection>,
1251 #[serde(rename = "EssentialProperty")]
1252 pub essential_property: Vec<EssentialProperty>,
1253 #[serde(rename = "SupplementalProperty")]
1254 pub supplemental_property: Vec<SupplementalProperty>,
1255 pub InbandEventStream: Vec<InbandEventStream>,
1256 pub Switching: Vec<Switching>,
1257 #[serde(rename = "GroupLabel")]
1259 pub group_label: Vec<Label>,
1260 pub Label: Vec<Label>,
1261 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1262 pub Resync: Vec<Resync>,
1264 pub BaseURL: Vec<BaseURL>,
1265 pub SubRepresentation: Vec<SubRepresentation>,
1267 pub SegmentBase: Option<SegmentBase>,
1268 pub SegmentList: Option<SegmentList>,
1269 pub SegmentTemplate: Option<SegmentTemplate>,
1270 #[serde(rename = "RepresentationIndex")]
1271 pub representation_index: Option<RepresentationIndex>,
1272}
1273
1274#[skip_serializing_none]
1276#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1277#[serde(default)]
1278pub struct ContentComponent {
1279 #[serde(rename = "@id")]
1280 pub id: Option<String>,
1281 #[serde(rename = "@lang")]
1283 pub lang: Option<String>,
1284 #[serde(rename = "@contentType")]
1285 pub contentType: Option<String>,
1286 #[serde(rename = "@par")]
1287 pub par: Option<String>,
1288 #[serde(rename = "@tag")]
1289 pub tag: Option<String>,
1290 pub Accessibility: Vec<Accessibility>,
1291 pub Role: Vec<Role>,
1292 pub Rating: Vec<Rating>,
1293 pub Viewpoint: Vec<Viewpoint>,
1294}
1295
1296#[skip_serializing_none]
1298#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1299#[serde(default)]
1300pub struct CencPssh {
1301 #[serde(rename = "$text")]
1302 pub content: Option<String>,
1303}
1304
1305#[skip_serializing_none]
1307#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1308#[serde(default)]
1309pub struct Laurl {
1310 #[serde(rename = "@Lic_type")]
1311 pub lic_type: Option<String>,
1312 #[serde(rename = "$text")]
1313 pub content: Option<String>,
1314}
1315
1316#[skip_serializing_none]
1318#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1319#[serde(default)]
1320pub struct MsprPro {
1321 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
1322 pub xmlns: Option<String>,
1323 #[serde(rename = "$text")]
1324 pub content: Option<String>,
1325}
1326
1327#[skip_serializing_none]
1328#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1329#[serde(default)]
1330pub struct MsprIsEncrypted {
1331 #[serde(rename = "$text")]
1332 pub content: Option<String>,
1333}
1334
1335#[skip_serializing_none]
1336#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1337#[serde(default)]
1338pub struct MsprIVSize {
1339 #[serde(rename = "$text")]
1340 pub content: Option<String>,
1341}
1342
1343#[skip_serializing_none]
1344#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1345#[serde(default)]
1346pub struct MsprKid {
1347 #[serde(rename = "$text")]
1348 pub content: Option<String>,
1349}
1350
1351#[skip_serializing_none]
1352#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1353#[serde(default)]
1354pub struct OutputProtection {
1355 #[serde(rename = "@schemeIdUri")]
1356 pub schemeIdUri: String,
1357 #[serde(rename = "@value")]
1358 pub value: Option<String>,
1359 #[serde(rename = "@id")]
1360 pub id: Option<String>,
1361}
1362
1363#[skip_serializing_none]
1368#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1369#[serde(default)]
1370pub struct ContentProtection {
1371 #[serde(rename = "@robustness")]
1373 pub robustness: Option<String>,
1374 #[serde(rename = "@refId")]
1375 pub refId: Option<String>,
1376 #[serde(rename = "@ref")]
1378 pub r#ref: Option<String>,
1379 #[serde(rename = "@schemeIdUri")]
1381 pub schemeIdUri: String,
1382 #[serde(rename = "@value")]
1383 pub value: Option<String>,
1384 #[serde(rename = "@id")]
1385 pub id: Option<String>,
1386 #[serde(rename="cenc:pssh", alias="pssh")]
1388 pub cenc_pssh: Vec<CencPssh>,
1389 #[serde(rename = "@cenc:default_KID", alias = "@default_KID")]
1391 pub default_KID: Option<String>,
1392 #[serde(rename = "dashif:laurl", alias = "laurl")]
1394 pub laurl: Option<Laurl>,
1395 #[serde(rename = "clearkey:Laurl", alias = "Laurl")]
1399 pub clearkey_laurl: Option<Laurl>,
1400 #[serde(rename = "mspr:pro", alias = "pro")]
1402 pub msprpro: Option<MsprPro>,
1403 #[serde(rename = "mspr:IsEncrypted", alias = "IsEncrypted")]
1404 pub mspr_is_encrypted: Option<MsprIsEncrypted>,
1405 #[serde(rename = "mspr:IV_Size", alias = "IV_Size")]
1406 pub mspr_iv_size: Option<MsprIVSize>,
1407 #[serde(rename = "mspr:kid", alias = "kid")]
1408 pub mspr_kid: Option<MsprKid>,
1409}
1410
1411#[skip_serializing_none]
1417#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1418#[serde(default)]
1419pub struct Role {
1420 #[serde(rename = "@id")]
1421 pub id: Option<String>,
1422 #[serde(rename = "@schemeIdUri")]
1423 pub schemeIdUri: String,
1424 #[serde(rename = "@value")]
1425 pub value: Option<String>,
1426}
1427
1428#[skip_serializing_none]
1429#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1430#[serde(default)]
1431pub struct Viewpoint {
1432 #[serde(rename = "@id")]
1433 pub id: Option<String>,
1434 #[serde(rename = "@schemeIdUri")]
1435 pub schemeIdUri: String,
1436 #[serde(rename = "@value")]
1437 pub value: Option<String>,
1438}
1439
1440#[skip_serializing_none]
1441#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1442#[serde(default)]
1443pub struct Selection {
1444 #[serde(rename = "@dataEncoding")]
1445 pub dataEncoding: Option<String>,
1446 #[serde(rename = "@parameter")]
1447 pub parameter: Option<String>,
1448 #[serde(rename = "@data")]
1449 pub data: Option<String>,
1450}
1451
1452#[skip_serializing_none]
1453#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1454#[serde(default)]
1455pub struct SelectionInfo {
1456 #[serde(rename = "@selectionInfo")]
1457 pub selectionInfo: Option<String>,
1458 #[serde(rename = "@contactURL")]
1459 pub contactURL: Option<String>,
1460 pub Selection: Vec<Selection>,
1461}
1462
1463#[skip_serializing_none]
1470#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1471#[serde(default)]
1472pub struct Event {
1473 #[serde(rename = "@id")]
1474 pub id: Option<String>,
1475 #[serde(rename = "@presentationTime", default = "default_optu64_zero")]
1476 pub presentationTime: Option<u64>,
1477 #[serde(rename = "@presentationTimeOffset")]
1478 pub presentationTimeOffset: Option<u64>,
1479 #[serde(rename = "@duration")]
1480 pub duration: Option<u64>,
1481 #[serde(rename = "@timescale")]
1482 pub timescale: Option<u64>,
1483 #[serde(rename = "@contentEncoding")]
1486 pub contentEncoding: Option<String>,
1487 #[serde(rename = "@messageData")]
1490 pub messageData: Option<String>,
1491 pub SelectionInfo: Option<SelectionInfo>,
1492 #[cfg(feature = "scte35")]
1493 #[serde(rename = "scte35:Signal", alias="Signal")]
1494 #[cfg(feature = "scte35")]
1495 pub signal: Vec<Signal>,
1496 #[cfg(feature = "scte35")]
1497 #[serde(rename = "scte35:SpliceInfoSection", alias="SpliceInfoSection")]
1498 #[cfg(feature = "scte35")]
1499 pub splice_info_section: Vec<SpliceInfoSection>,
1500 #[serde(rename = "@value")]
1503 pub value: Option<String>,
1504 #[serde(rename = "$text")]
1507 pub content: Option<String>,
1508}
1509
1510#[skip_serializing_none]
1511#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1512#[serde(default)]
1513pub struct EventStream {
1514 #[serde(rename = "@xlink:href")]
1515 #[serde(alias = "@href")]
1516 pub href: Option<String>,
1517 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1518 pub actuate: Option<String>,
1519 #[serde(rename = "@messageData")]
1520 pub messageData: Option<String>,
1522 #[serde(rename = "@schemeIdUri")]
1523 pub schemeIdUri: String,
1524 #[serde(rename = "@value")]
1525 pub value: Option<String>,
1526 #[serde(rename = "@timescale")]
1527 pub timescale: Option<u64>,
1528 #[serde(rename = "@presentationTimeOffset")]
1529 pub presentationTimeOffset: Option<u64>,
1530 #[serde(rename = "Event")]
1531 pub event: Vec<Event>,
1532}
1533
1534#[skip_serializing_none]
1540#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1541#[serde(default)]
1542pub struct InbandEventStream {
1543 #[serde(rename = "@timescale")]
1544 pub timescale: Option<u64>,
1545 #[serde(rename = "@schemeIdUri")]
1546 pub schemeIdUri: String,
1547 #[serde(rename = "Event")]
1548 pub event: Vec<Event>,
1549 #[serde(rename = "@value")]
1550 pub value: Option<String>,
1551 #[serde(rename = "@xlink:href")]
1553 #[serde(alias = "@href")]
1554 pub href: Option<String>,
1555 #[serde(rename = "@xlink:actuate", alias = "@actuate")]
1556 pub actuate: Option<String>,
1557}
1558
1559#[skip_serializing_none]
1560#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1561#[serde(default)]
1562pub struct EssentialProperty {
1563 #[serde(rename = "@id")]
1564 pub id: Option<String>,
1565 #[serde(rename = "@schemeIdUri")]
1566 pub schemeIdUri: String,
1567 #[serde(rename = "@value")]
1568 pub value: Option<String>,
1569}
1570
1571#[skip_serializing_none]
1572#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1573#[serde(default)]
1574pub struct SupplementalProperty {
1575 #[serde(rename = "@id")]
1576 pub id: Option<String>,
1577 #[serde(rename = "@schemeIdUri")]
1578 pub schemeIdUri: String,
1579 #[serde(rename = "@value")]
1580 pub value: Option<String>,
1581 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1582 #[serde(rename(deserialize = "ContentIdentifier"))]
1583 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1584}
1585
1586#[skip_serializing_none]
1589#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1590#[serde(default)]
1591pub struct Label {
1592 #[serde(rename = "@id")]
1593 pub id: Option<String>,
1594 #[serde(rename = "@lang")]
1595 pub lang: Option<String>,
1596 #[serde(rename = "$text")]
1597 pub content: String,
1598}
1599
1600#[skip_serializing_none]
1608#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1609#[serde(default)]
1610pub struct AdaptationSet {
1611 #[serde(rename = "@id")]
1612 pub id: Option<String>,
1613 #[serde(rename = "@xlink:href", alias = "@href")]
1615 pub href: Option<String>,
1616 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1617 pub actuate: Option<String>,
1618 #[serde(rename = "@group")]
1619 pub group: Option<i64>,
1620 #[serde(rename = "@selectionPriority")]
1621 pub selectionPriority: Option<u64>,
1622 #[serde(rename = "@contentType")]
1624 pub contentType: Option<String>,
1625 #[serde(rename = "@profiles")]
1626 pub profiles: Option<String>,
1627 #[serde(rename = "@lang")]
1629 pub lang: Option<String>,
1630 #[serde(rename = "@sar")]
1632 pub sar: Option<String>,
1633 #[serde(rename = "@par")]
1635 pub par: Option<String>,
1636 #[serde(rename = "@scanType")]
1638 pub scanType: Option<String>,
1639 #[serde(rename = "@segmentAlignment")]
1640 pub segmentAlignment: Option<bool>,
1641 #[serde(rename = "@segmentProfiles")]
1642 pub segmentProfiles: Option<String>,
1645 #[serde(rename = "@subsegmentAlignment")]
1646 pub subsegmentAlignment: Option<bool>,
1647 #[serde(rename = "@subsegmentStartsWithSAP")]
1648 pub subsegmentStartsWithSAP: Option<u64>,
1649 #[serde(rename = "@bitstreamSwitching")]
1650 pub bitstreamSwitching: Option<bool>,
1651 #[serde(rename = "@audioSamplingRate")]
1652 pub audioSamplingRate: Option<String>,
1653 #[serde(rename = "@width")]
1654 pub width: Option<u64>,
1655 #[serde(rename = "@height")]
1656 pub height: Option<u64>,
1657 #[serde(rename = "@mimeType")]
1659 pub mimeType: Option<String>,
1660 #[serde(rename = "@codecs")]
1662 pub codecs: Option<String>,
1663 #[serde(rename = "@minBandwidth")]
1664 pub minBandwidth: Option<u64>,
1665 #[serde(rename = "@maxBandwidth")]
1666 pub maxBandwidth: Option<u64>,
1667 #[serde(rename = "@minWidth")]
1668 pub minWidth: Option<u64>,
1669 #[serde(rename = "@maxWidth")]
1670 pub maxWidth: Option<u64>,
1671 #[serde(rename = "@minHeight")]
1672 pub minHeight: Option<u64>,
1673 #[serde(rename = "@maxHeight")]
1674 pub maxHeight: Option<u64>,
1675 #[serde(rename = "@frameRate")]
1676 pub frameRate: Option<String>, #[serde(rename = "@minFrameRate")]
1678 pub minFrameRate: Option<String>, #[serde(rename = "@maxFrameRate")]
1680 pub maxFrameRate: Option<String>, #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1683 pub maxPlayoutRate: Option<f64>,
1684 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1685 pub maximumSAPPeriod: Option<f64>,
1686 #[serde(rename = "@startWithSAP")]
1687 pub startWithSAP: Option<u64>,
1688 #[serde(rename = "@codingDependency")]
1689 pub codingDependency: Option<bool>,
1690 pub FramePacking: Vec<FramePacking>,
1691 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1692 pub ContentProtection: Vec<ContentProtection>,
1693 #[serde(rename = "EssentialProperty")]
1695 pub essential_property: Vec<EssentialProperty>,
1696 #[serde(rename = "SupplementalProperty")]
1697 pub supplemental_property: Vec<SupplementalProperty>,
1698 pub InbandEventStream: Vec<InbandEventStream>,
1699 pub Switching: Vec<Switching>,
1700 pub GroupLabel: Vec<Label>,
1702 pub Label: Vec<Label>,
1703 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1704 pub Resync: Vec<Resync>,
1706 pub Accessibility: Vec<Accessibility>,
1707 pub Role: Vec<Role>,
1708 pub Rating: Vec<Rating>,
1709 pub Viewpoint: Vec<Viewpoint>,
1710 pub ContentComponent: Vec<ContentComponent>,
1711 pub BaseURL: Vec<BaseURL>,
1712 pub SegmentBase: Option<SegmentBase>,
1713 pub SegmentList: Option<SegmentList>,
1714 pub SegmentTemplate: Option<SegmentTemplate>,
1715 #[serde(rename = "Representation")]
1716 pub representations: Vec<Representation>,
1717 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1718 pub scte214_supplemental_profiles: Option<String>,
1719 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1720 pub scte214_supplemental_codecs: Option<String>,
1721}
1722
1723#[skip_serializing_none]
1728#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1729#[serde(default)]
1730pub struct AssetIdentifier {
1731 #[serde(rename = "@schemeIdUri")]
1732 pub schemeIdUri: String,
1733 #[serde(rename = "@value")]
1734 pub value: Option<String>,
1735 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1736 #[serde(rename(deserialize = "ContentIdentifier"))]
1737 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1738}
1739
1740#[skip_serializing_none]
1745#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1746#[serde(default)]
1747pub struct Subset {
1748 #[serde(rename = "@id")]
1749 pub id: Option<String>,
1750 #[serde(rename = "@contains",
1753 deserialize_with = "deserialize_xsd_uintvector",
1754 serialize_with = "serialize_xsd_uintvector",
1755 default)]
1756 pub contains: Vec<u64>,
1757}
1758
1759#[skip_serializing_none]
1762#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1763#[serde(default)]
1764pub struct Period {
1765 #[serde(rename = "@xlink:href", alias = "@href")]
1767 pub href: Option<String>,
1768
1769 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
1770 pub actuate: Option<String>,
1771
1772 #[serde(rename = "@id")]
1773 pub id: Option<String>,
1774
1775 #[serde(rename = "@start",
1777 serialize_with = "serialize_xs_duration",
1778 deserialize_with = "deserialize_xs_duration",
1779 default)]
1780 pub start: Option<Duration>,
1781
1782 #[serde(rename = "@duration",
1784 serialize_with = "serialize_xs_duration",
1785 deserialize_with = "deserialize_xs_duration",
1786 default)]
1787 pub duration: Option<Duration>,
1788
1789 #[serde(rename = "@bitstreamSwitching", default)]
1791 pub bitstreamSwitching: Option<bool>,
1792
1793 pub BaseURL: Vec<BaseURL>,
1794
1795 pub SegmentBase: Option<SegmentBase>,
1796
1797 pub SegmentList: Option<SegmentList>,
1798
1799 pub SegmentTemplate: Option<SegmentTemplate>,
1800
1801 #[serde(rename = "AssetIdentifier")]
1802 pub asset_identifier: Option<AssetIdentifier>,
1803
1804 #[serde(rename = "EventStream")]
1805 pub event_streams: Vec<EventStream>,
1806
1807 #[serde(rename = "ServiceDescription")]
1808 pub service_description: Vec<ServiceDescription>,
1809
1810 pub ContentProtection: Vec<ContentProtection>,
1811
1812 #[serde(rename = "AdaptationSet")]
1813 pub adaptations: Vec<AdaptationSet>,
1814
1815 #[serde(rename = "Subset")]
1816 pub subsets: Vec<Subset>,
1817
1818 #[serde(rename = "SupplementalProperty")]
1819 pub supplemental_property: Vec<SupplementalProperty>,
1820
1821 #[serde(rename = "EmptyAdaptationSet")]
1822 pub empty_adaptations: Vec<AdaptationSet>,
1823
1824 #[serde(rename = "GroupLabel")]
1825 pub group_label: Vec<Label>,
1826
1827 #[serde(rename = "Preselection")]
1828 pub pre_selections: Vec<Preselection>,
1829
1830 #[serde(rename = "EssentialProperty")]
1831 pub essential_property: Vec<EssentialProperty>,
1832}
1833
1834#[skip_serializing_none]
1835#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1836#[serde(default)]
1837pub struct Reporting {
1838 #[serde(rename = "@id")]
1839 pub id: Option<String>,
1840 #[serde(rename = "@schemeIdUri")]
1841 pub schemeIdUri: String,
1842 #[serde(rename = "@value")]
1843 pub value: Option<String>,
1844 #[serde(rename = "@dvb:reportingUrl", alias = "@reportingUrl")]
1845 pub reportingUrl: Option<String>,
1846 #[serde(rename = "@dvb:probability", alias = "@probability")]
1847 pub probability: Option<u64>,
1848}
1849
1850#[skip_serializing_none]
1851#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1852#[serde(default)]
1853pub struct Range {
1854 #[serde(rename = "@starttime",
1855 serialize_with = "serialize_xs_duration",
1856 deserialize_with = "deserialize_xs_duration",
1857 default)]
1858 pub starttime: Option<Duration>,
1859 #[serde(rename = "@duration",
1860 serialize_with = "serialize_xs_duration",
1861 deserialize_with = "deserialize_xs_duration",
1862 default)]
1863 pub duration: Option<Duration>,
1864}
1865
1866#[skip_serializing_none]
1867#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1868#[serde(default)]
1869pub struct Metrics {
1870 #[serde(rename = "@metrics")]
1871 pub metrics: String,
1872 pub Reporting: Vec<Reporting>,
1873 pub Range: Vec<Range>,
1874}
1875
1876#[skip_serializing_none]
1878#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1879#[serde(default)]
1880pub struct Latency {
1881 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1882 pub min: Option<f64>,
1883 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1884 pub max: Option<f64>,
1885 #[serde(rename = "@target", serialize_with="serialize_opt_xsd_double")]
1886 pub target: Option<f64>,
1887 #[serde(rename = "@referenceId")]
1888 pub referenceId: Option<String>,
1889}
1890
1891#[skip_serializing_none]
1893#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1894#[serde(default)]
1895pub struct PlaybackRate {
1896 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1897 pub min: Option<f64>,
1898 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1899 pub max: Option<f64>,
1900}
1901
1902#[skip_serializing_none]
1904#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1905#[serde(default)]
1906pub struct OperatingQuality {
1907 #[serde(default = "default_optstring_any")]
1908 pub mediaType: Option<String>,
1909 #[serde(rename = "@min")]
1910 pub min: Option<u64>,
1911 #[serde(rename = "@max")]
1912 pub max: Option<u64>,
1913 #[serde(rename = "@target")]
1914 pub target: Option<u64>,
1915 #[serde(rename = "@type")]
1916 pub _type: Option<String>,
1917 #[serde(rename = "@maxDifference")]
1918 pub maxDifference: Option<u64>,
1919}
1920
1921#[skip_serializing_none]
1923#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1924#[serde(default)]
1925pub struct OperatingBandwidth {
1926 #[serde(rename = "@mediaType", default = "default_optstring_any")]
1927 pub mediaType: Option<String>,
1928 #[serde(rename = "@min")]
1929 pub min: Option<u64>,
1930 #[serde(rename = "@max")]
1931 pub max: Option<u64>,
1932 #[serde(rename = "@target")]
1933 pub target: Option<u64>,
1934}
1935
1936#[skip_serializing_none]
1937#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1938#[serde(default)]
1939pub struct ContentSteering {
1940 #[serde(rename = "@defaultServiceLocation")]
1941 pub defaultServiceLocation: Option<String>,
1942 #[serde(rename = "@queryBeforeStart", default = "default_optbool_false")]
1943 pub queryBeforeStart: Option<bool>,
1944 #[serde(rename = "@clientRequirement", default = "default_optbool_true")]
1945 pub clientRequirement: Option<bool>,
1946}
1947
1948#[skip_serializing_none]
1949#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1950#[serde(default)]
1951pub struct CMCDParameters {
1952 #[serde(rename = "@version", default = "default_optu64_one")]
1953 pub version: Option<u64>,
1954 #[serde(rename = "@mode", default = "default_optstring_query")]
1955 pub mode: Option<String>,
1956 #[serde(rename = "@includeInRequests", default = "default_optstring_segment")]
1957 pub includeInRequests: Option<String>,
1958 #[serde(rename = "@keys")]
1959 pub keys: String,
1960 #[serde(rename = "@contentID")]
1961 pub contentID: Option<String>,
1962 #[serde(rename = "@sessionID")]
1963 pub sessionID: Option<String>,
1964}
1965
1966#[skip_serializing_none]
1968#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1969#[serde(default)]
1970pub struct ClientDataReporting {
1971 pub CMCDParameters: Vec<CMCDParameters>,
1972 #[serde(rename = "@serviceLocations")]
1973 pub serviceLocations: Option<String>,
1974 #[serde(rename = "@adaptationSets")]
1975 pub adaptationSets: Option<String>,
1976}
1977
1978#[skip_serializing_none]
1979#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1980#[serde(default)]
1981pub struct PlaybackRestrictions {
1982 #[serde(rename = "@skipAfter",
1983 serialize_with = "serialize_xs_duration",
1984 deserialize_with = "deserialize_xs_duration",
1985 default)]
1986 pub skipAfter: Option<Duration>,
1987}
1988
1989#[skip_serializing_none]
1990#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1991#[serde(default)]
1992pub struct ServiceDescription {
1993 #[serde(rename = "Scope")]
1994 pub scopes: Vec<Scope>,
1995 pub Latency: Vec<Latency>,
1996 pub PlaybackRate: Vec<PlaybackRate>,
1997 pub OperatingQuality: Vec<OperatingQuality>,
1998 pub OperatingBandwidth: Vec<OperatingBandwidth>,
1999 pub ContentSteering: Vec<ContentSteering>,
2000 pub ClientDataReporting: Vec<ClientDataReporting>,
2001 pub PlaybackRestrictions: Vec<PlaybackRestrictions>,
2002 #[serde(rename = "@id")]
2003 pub id: Option<String>,
2004}
2005
2006#[skip_serializing_none]
2008#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2009#[serde(default)]
2010pub struct UTCTiming {
2011 #[serde(rename = "@id")]
2012 pub id: Option<String>,
2013 #[serde(rename = "@schemeIdUri")]
2016 pub schemeIdUri: String,
2017 #[serde(rename = "@value")]
2018 pub value: Option<String>,
2019}
2020
2021#[skip_serializing_none]
2026#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2027#[serde(default)]
2028pub struct ProducerReferenceTime {
2029 #[serde(rename = "@id")]
2031 pub id: Option<String>,
2032 #[serde(rename = "@inband", default = "default_optbool_false")]
2033 pub inband: Option<bool>,
2034 #[serde(rename = "@presentationTime")]
2036 pub presentationTime: Option<u64>,
2037 #[serde(rename = "@type", default = "default_optstring_encoder")]
2038 pub prtType: Option<String>,
2039 #[serde(rename = "@wallClockTime",
2043 alias="@wallclockTime",
2044 deserialize_with = "deserialize_xs_datetime",
2045 default)]
2046 pub wallClockTime: Option<XsDatetime>,
2047 pub UTCTiming: Option<UTCTiming>,
2048}
2049
2050#[skip_serializing_none]
2051#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2052#[serde(default)]
2053pub struct LeapSecondInformation {
2054 #[serde(rename = "@availabilityStartLeapOffset")]
2055 pub availabilityStartLeapOffset: Option<i64>,
2056 #[serde(rename = "@nextAvailabilityStartLeapOffset")]
2057 pub nextAvailabilityStartLeapOffset: Option<i64>,
2058 #[serde(rename = "@nextLeapChangeTime",
2059 deserialize_with = "deserialize_xs_datetime",
2060 default)]
2061 pub nextLeapChangeTime: Option<XsDatetime>,
2062}
2063
2064#[skip_serializing_none]
2071#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2072#[serde(default)]
2073pub struct PatchLocation {
2074 #[serde(rename = "@ttl", serialize_with="serialize_opt_xsd_double")]
2075 pub ttl: Option<f64>,
2076 #[serde(rename = "$text")]
2077 pub content: String,
2078}
2079
2080#[skip_serializing_none]
2082#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2083#[serde(default)]
2084pub struct MPD {
2085 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
2086 pub xmlns: Option<String>,
2087 #[serde(rename = "@id")]
2088 pub id: Option<String>,
2089 #[serde(rename = "@profiles")]
2090 pub profiles: Option<String>,
2091 #[serde(rename = "@type")]
2094 pub mpdtype: Option<String>,
2095 #[serde(rename = "@availabilityStartTime",
2096 deserialize_with = "deserialize_xs_datetime",
2097 default)]
2098 pub availabilityStartTime: Option<XsDatetime>,
2099 #[serde(rename = "@availabilityEndTime",
2100 deserialize_with = "deserialize_xs_datetime",
2101 default)]
2102 pub availabilityEndTime: Option<XsDatetime>,
2103 #[serde(rename = "@publishTime",
2104 deserialize_with = "deserialize_xs_datetime",
2105 default)]
2106 pub publishTime: Option<XsDatetime>,
2107 #[serde(rename = "@mediaPresentationDuration",
2108 serialize_with = "serialize_xs_duration",
2109 deserialize_with = "deserialize_xs_duration",
2110 default)]
2111 pub mediaPresentationDuration: Option<Duration>,
2112 #[serde(rename = "@minimumUpdatePeriod",
2113 serialize_with = "serialize_xs_duration",
2114 deserialize_with = "deserialize_xs_duration",
2115 default)]
2116 pub minimumUpdatePeriod: Option<Duration>,
2117 #[serde(rename = "@minBufferTime",
2119 serialize_with = "serialize_xs_duration",
2120 deserialize_with = "deserialize_xs_duration",
2121 default)]
2122 pub minBufferTime: Option<Duration>,
2123 #[serde(rename = "@timeShiftBufferDepth",
2126 serialize_with = "serialize_xs_duration",
2127 deserialize_with = "deserialize_xs_duration",
2128 default)]
2129 pub timeShiftBufferDepth: Option<Duration>,
2130 #[serde(rename = "@suggestedPresentationDelay",
2132 serialize_with = "serialize_xs_duration",
2133 deserialize_with = "deserialize_xs_duration",
2134 default)]
2135 pub suggestedPresentationDelay: Option<Duration>,
2136 #[serde(rename = "@maxSegmentDuration",
2137 serialize_with = "serialize_xs_duration",
2138 deserialize_with = "deserialize_xs_duration",
2139 default)]
2140 pub maxSegmentDuration: Option<Duration>,
2141 #[serde(rename = "@maxSubsegmentDuration",
2142 serialize_with = "serialize_xs_duration",
2143 deserialize_with = "deserialize_xs_duration",
2144 default)]
2145 pub maxSubsegmentDuration: Option<Duration>,
2146 #[serialize_always]
2148 #[serde(rename="@xmlns:xsi", alias="@xsi", serialize_with="serialize_xsi_ns")]
2149 pub xsi: Option<String>,
2150 #[serde(alias = "@ext", rename = "@xmlns:ext")]
2151 pub ext: Option<String>,
2152 #[serialize_always]
2154 #[serde(rename="@xmlns:cenc", alias="@cenc", serialize_with="serialize_cenc_ns")]
2155 pub cenc: Option<String>,
2156 #[serialize_always]
2158 #[serde(rename="@xmlns:mspr", alias="@mspr", serialize_with="serialize_mspr_ns")]
2159 pub mspr: Option<String>,
2160 #[serialize_always]
2162 #[serde(rename="@xmlns:xlink", alias="@xlink", serialize_with="serialize_xlink_ns")]
2163 pub xlink: Option<String>,
2164 #[cfg(feature = "scte35")]
2167 #[serialize_always]
2168 #[serde(rename="@xmlns:scte35", alias="@scte35", serialize_with="scte35::serialize_scte35_ns")]
2169 pub scte35: Option<String>,
2170 #[serialize_always]
2173 #[serde(rename="@xmlns:dvb", alias="@dvb", serialize_with="serialize_dvb_ns")]
2174 pub dvb: Option<String>,
2175 #[serde(rename = "@xsi:schemaLocation", alias = "@schemaLocation")]
2176 pub schemaLocation: Option<String>,
2177 #[serde(alias = "@scte214", rename = "@xmlns:scte214")]
2179 pub scte214: Option<String>,
2180 pub ProgramInformation: Vec<ProgramInformation>,
2181 #[serde(rename = "BaseURL")]
2183 pub base_url: Vec<BaseURL>,
2184 #[serde(rename = "Location", default)]
2185 pub locations: Vec<Location>,
2186 pub PatchLocation: Vec<PatchLocation>,
2189 pub ServiceDescription: Vec<ServiceDescription>,
2190 pub ContentProtection: Vec<ContentProtection>,
2192 #[serde(rename = "Period", default)]
2193 pub periods: Vec<Period>,
2194 pub Metrics: Vec<Metrics>,
2195 #[serde(rename = "EssentialProperty")]
2196 pub essential_property: Vec<EssentialProperty>,
2197 #[serde(rename = "SupplementalProperty")]
2198 pub supplemental_property: Vec<SupplementalProperty>,
2199 pub UTCTiming: Vec<UTCTiming>,
2200 pub LeapSecondInformation: Option<LeapSecondInformation>,
2202}
2203
2204impl std::fmt::Display for MPD {
2205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2206 write!(f, "{}", quick_xml::se::to_string(self).map_err(|_| std::fmt::Error)?)
2207 }
2208}
2209
2210pub fn parse(xml: &str) -> Result<MPD, DashMpdError> {
2212 #[cfg(feature = "warn_ignored_elements")]
2213 {
2214 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2215 let _: MPD = serde_ignored::deserialize(xd, |path| {
2216 warn!("Unused XML element in manifest: {path}");
2217 }).map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2218 }
2219 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2220 let mpd: MPD = serde_path_to_error::deserialize(xd)
2221 .map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2222 Ok(mpd)
2223}
2224
2225
2226fn is_audio_codec(name: &str) -> bool {
2228 name.starts_with("mp4a") ||
2229 name.starts_with("aac") ||
2230 name.starts_with("vorbis") ||
2231 name.starts_with("opus") ||
2232 name.starts_with("ogg") ||
2233 name.starts_with("webm") ||
2234 name.starts_with("flac") ||
2235 name.starts_with("mp3") ||
2236 name.starts_with("mpeg") ||
2237 name.starts_with("3gpp") ||
2238 name.starts_with("wav") ||
2239 name.starts_with("ec-3") ||
2240 name.starts_with("ac-4") ||
2241 name.starts_with("dtsc") ||
2242 name.starts_with("aptx") ||
2243 name.starts_with("aiff") ||
2244 name.starts_with("mha1") }
2246
2247
2248pub fn is_audio_adaptation(a: &&AdaptationSet) -> bool {
2254 if let Some(codec) = &a.codecs {
2255 if is_audio_codec(codec) {
2256 return true;
2257 }
2258 }
2259 if let Some(ct) = &a.contentType {
2260 if ct == "audio" {
2261 return true;
2262 }
2263 }
2264 if let Some(mimetype) = &a.mimeType {
2265 if mimetype.starts_with("audio/") {
2266 return true;
2267 }
2268 }
2269 for r in &a.representations {
2270 if let Some(ct) = &r.contentType {
2271 if ct == "audio" {
2272 return true;
2273 }
2274 }
2275 if let Some(mimetype) = &r.mimeType {
2276 if mimetype.starts_with("audio/") {
2277 return true;
2278 }
2279 }
2280 }
2281 false
2282}
2283
2284pub fn is_video_adaptation(a: &&AdaptationSet) -> bool {
2293 if is_audio_adaptation(a) {
2294 return false;
2295 }
2296 if let Some(ct) = &a.contentType {
2297 if ct == "video" {
2298 return true;
2299 }
2300 }
2301 if let Some(mimetype) = &a.mimeType {
2302 if mimetype.starts_with("video/") {
2303 return true;
2304 }
2305 }
2306 for r in &a.representations {
2307 if let Some(ct) = &r.contentType {
2308 if ct == "video" {
2309 return true;
2310 }
2311 }
2312 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2315 return false;
2316 }
2317 if let Some(mimetype) = &r.mimeType {
2318 if mimetype.starts_with("video/") {
2319 return true;
2320 }
2321 }
2322 }
2323 false
2324}
2325
2326
2327fn is_subtitle_mimetype(mt: &str) -> bool {
2328 mt.eq("text/vtt") ||
2329 mt.eq("application/ttml+xml") ||
2330 mt.eq("application/x-sami")
2331
2332 }
2335
2336fn is_subtitle_codec(c: &str) -> bool {
2337 c == "wvtt" ||
2338 c == "c608" ||
2339 c == "stpp" ||
2340 c == "tx3g" ||
2341 c.starts_with("stpp.")
2342}
2343
2344pub fn is_subtitle_adaptation(a: &&AdaptationSet) -> bool {
2356 if a.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2357 return true;
2358 }
2359 if a.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2360 return true;
2361 }
2362 if a.codecs.as_deref().is_some_and(is_subtitle_codec) {
2363 return true;
2364 }
2365 for cc in a.ContentComponent.iter() {
2366 if cc.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2367 return true;
2368 }
2369 }
2370 for r in a.representations.iter() {
2371 if r.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2372 return true;
2373 }
2374 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2376 return true;
2377 }
2378 }
2379 false
2380}
2381
2382
2383#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2385pub enum SubtitleType {
2386 Vtt,
2388 Srt,
2390 Sub,
2392 Ass,
2394 Ttxt,
2396 Ttml,
2398 Sami,
2400 Wvtt,
2404 Stpp,
2406 Eia608,
2408 Unknown,
2409}
2410
2411fn subtitle_type_for_mimetype(mt: &str) -> Option<SubtitleType> {
2412 match mt {
2413 "text/vtt" => Some(SubtitleType::Vtt),
2414 "application/ttml+xml" => Some(SubtitleType::Ttml),
2415 "application/x-sami" => Some(SubtitleType::Sami),
2416 _ => None
2417 }
2418}
2419
2420#[must_use]
2421pub fn subtitle_type(a: &&AdaptationSet) -> SubtitleType {
2422 if let Some(mimetype) = &a.mimeType {
2423 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2424 return st;
2425 }
2426 }
2427 if let Some(codecs) = &a.codecs {
2428 if codecs == "wvtt" {
2429 return SubtitleType::Wvtt;
2431 }
2432 if codecs == "c608" {
2433 return SubtitleType::Eia608;
2434 }
2435 if codecs == "tx3g" {
2436 return SubtitleType::Ttxt;
2437 }
2438 if codecs == "stpp" {
2439 return SubtitleType::Stpp;
2440 }
2441 if codecs.starts_with("stpp.") {
2442 return SubtitleType::Stpp;
2443 }
2444 }
2445 for r in a.representations.iter() {
2446 if let Some(mimetype) = &r.mimeType {
2447 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2448 return st;
2449 }
2450 }
2451 if let Some(codecs) = &r.codecs {
2452 if codecs == "wvtt" {
2453 return SubtitleType::Wvtt;
2454 }
2455 if codecs == "c608" {
2456 return SubtitleType::Eia608;
2457 }
2458 if codecs == "tx3g" {
2459 return SubtitleType::Ttxt;
2460 }
2461 if codecs == "stpp" {
2462 return SubtitleType::Stpp;
2463 }
2464 if codecs.starts_with("stpp.") {
2465 return SubtitleType::Stpp;
2466 }
2467 }
2468 }
2469 SubtitleType::Unknown
2470}
2471
2472
2473#[allow(dead_code)]
2474fn content_protection_type(cp: &ContentProtection) -> String {
2475 if let Some(v) = &cp.value {
2476 if v.eq("cenc") {
2477 return String::from("cenc");
2478 }
2479 if v.eq("Widevine") {
2480 return String::from("Widevine");
2481 }
2482 if v.eq("MSPR 2.0") {
2483 return String::from("PlayReady");
2484 }
2485 }
2486 let uri = &cp.schemeIdUri;
2488 let uri = uri.to_lowercase();
2489 if uri.eq("urn:mpeg:dash:mp4protection:2011") {
2490 return String::from("cenc");
2491 }
2492 if uri.eq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed") {
2493 return String::from("Widevine");
2494 }
2495 if uri.eq("urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95") {
2496 return String::from("PlayReady");
2497 }
2498 if uri.eq("urn:uuid:94ce86fb-07ff-4f43-adb8-93d2fa968ca2") {
2499 return String::from("FairPlay");
2500 }
2501 if uri.eq("urn:uuid:3ea8778f-7742-4bf9-b18b-e834b2acbd47") {
2502 return String::from("Clear Key AES-128");
2503 }
2504 if uri.eq("urn:uuid:be58615b-19c4-4684-88b3-c8c57e99e957") {
2505 return String::from("Clear Key SAMPLE-AES");
2506 }
2507 if uri.eq("urn:uuid:adb41c24-2dbf-4a6d-958b-4457c0d27b95") {
2508 return String::from("Nagra");
2509 }
2510 if uri.eq("urn:uuid:5e629af5-38da-4063-8977-97ffbd9902d4") {
2511 return String::from("Marlin");
2512 }
2513 if uri.eq("urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb") {
2514 return String::from("Adobe PrimeTime");
2515 }
2516 if uri.eq("urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b") {
2517 return String::from("W3C Common PSSH box");
2518 }
2519 if uri.eq("urn:uuid:80a6be7e-1448-4c37-9e70-d5aebe04c8d2") {
2520 return String::from("Irdeto Content Protection");
2521 }
2522 if uri.eq("urn:uuid:3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c") {
2523 return String::from("WisePlay-ChinaDRM");
2524 }
2525 if uri.eq("urn:uuid:616c7469-6361-7374-2d50-726f74656374") {
2526 return String::from("Alticast");
2527 }
2528 if uri.eq("urn:uuid:6dd8b3c3-45f4-4a68-bf3a-64168d01a4a6") {
2529 return String::from("ABV DRM");
2530 }
2531 if uri.eq("urn:mpeg:dash:sea:2012") {
2533 return String::from("SEA");
2534 }
2535 String::from("<unknown>")
2536}
2537
2538
2539fn check_segment_template_duration(
2540 st: &SegmentTemplate,
2541 max_seg_duration: &Duration,
2542 outer_timescale: u64) -> Vec<String>
2543{
2544 let mut errors = Vec::new();
2545 if let Some(timeline) = &st.SegmentTimeline {
2546 for s in &timeline.segments {
2547 let sd = s.d / st.timescale.unwrap_or(outer_timescale);
2548 if sd > max_seg_duration.as_secs() {
2549 errors.push(String::from("SegmentTimeline has segment@d > @maxSegmentDuration"));
2550 }
2551 }
2552 }
2553 errors
2554}
2555
2556fn check_segment_template_conformity(st: &SegmentTemplate) -> Vec<String> {
2557 let mut errors = Vec::new();
2558 if let Some(md) = &st.media {
2559 if !valid_url_p(md) {
2560 errors.push(format!("invalid URL {md}"));
2561 }
2562 if md.contains("$Number$") && md.contains("$Time") {
2563 errors.push(String::from("both $Number$ and $Time$ are used in media template URL"));
2564 }
2565 }
2566 if let Some(init) = &st.initialization {
2567 if !valid_url_p(init) {
2568 errors.push(format!("invalid URL {init}"));
2569 }
2570 if init.contains("$Number") {
2571 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2572 }
2573 if init.contains("$Time") {
2574 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2575 }
2576 }
2577 if st.duration.is_some() && st.SegmentTimeline.is_some() {
2578 errors.push(String::from("both SegmentTemplate.duration and SegmentTemplate.SegmentTimeline present"));
2579 }
2580 errors
2581}
2582
2583
2584fn valid_url_p(u: &str) -> bool {
2587 use url::ParseError;
2588
2589 match Url::parse(u) {
2590 Ok(url) => {
2591 url.scheme() == "https" ||
2592 url.scheme() == "http" ||
2593 url.scheme() == "ftp" ||
2594 url.scheme() == "file" ||
2595 url.scheme() == "data"
2596 },
2597 Err(ParseError::RelativeUrlWithoutBase) => true,
2598 Err(_) => false,
2599 }
2600}
2601
2602#[must_use]
2604pub fn check_conformity(mpd: &MPD) -> Vec<String> {
2605 let mut errors = Vec::new();
2606
2607 for p in &mpd.periods {
2610 if p.adaptations.is_empty() {
2611 errors.push(format!("Period with @id {} contains no AdaptationSet elements",
2612 p.id.clone().unwrap_or(String::from("<unspecified>"))));
2613 }
2614 for a in &p.adaptations {
2615 if let Some(mh) = a.maxHeight {
2616 if let Some(mr) = a.representations.iter().max_by_key(|r| r.height.unwrap_or(0)) {
2617 if mr.height.unwrap_or(0) > mh {
2618 errors.push(String::from("invalid @maxHeight on AdaptationSet"));
2619 }
2620 }
2621 }
2622 }
2623 }
2624 for p in &mpd.periods {
2627 for a in &p.adaptations {
2628 if let Some(mw) = a.maxWidth {
2629 if let Some(mr) = a.representations.iter().max_by_key(|r| r.width.unwrap_or(0)) {
2630 if mr.width.unwrap_or(0) > mw {
2631 errors.push(String::from("invalid @maxWidth on AdaptationSet"));
2632 }
2633 }
2634 }
2635 }
2636 }
2637 for p in &mpd.periods {
2640 for a in &p.adaptations {
2641 if let Some(mb) = a.maxBandwidth {
2642 if let Some(mr) = a.representations.iter().max_by_key(|r| r.bandwidth.unwrap_or(0)) {
2643 if mr.bandwidth.unwrap_or(0) > mb {
2644 errors.push(String::from("invalid @maxBandwidth on AdaptationSet"));
2645 }
2646 }
2647 }
2648 }
2649 }
2650 if let Some(max_seg_duration) = mpd.maxSegmentDuration {
2652 for p in &mpd.periods {
2653 for a in &p.adaptations {
2654 let mut outer_timescale = 1;
2665 if let Some(st) = &a.SegmentTemplate {
2666 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2667 .into_iter()
2668 .for_each(|msg| errors.push(msg));
2669 if let Some(ots) = st.timescale {
2670 outer_timescale = ots;
2671 }
2672 }
2673 for r in &a.representations {
2674 if let Some(st) = &r.SegmentTemplate {
2675 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2676 .into_iter()
2677 .for_each(|msg| errors.push(msg));
2678 }
2679 }
2680 }
2681 }
2682 }
2683
2684 for bu in &mpd.base_url {
2685 if !valid_url_p(&bu.base) {
2686 errors.push(format!("invalid URL {}", &bu.base));
2687 }
2688 }
2689 for p in &mpd.periods {
2690 for bu in &p.BaseURL {
2691 if !valid_url_p(&bu.base) {
2692 errors.push(format!("invalid URL {}", &bu.base));
2693 }
2694 }
2695 for a in &p.adaptations {
2696 for bu in &a.BaseURL {
2697 if !valid_url_p(&bu.base) {
2698 errors.push(format!("invalid URL {}", &bu.base));
2699 }
2700 }
2701 if let Some(st) = &a.SegmentTemplate {
2702 check_segment_template_conformity(st)
2703 .into_iter()
2704 .for_each(|msg| errors.push(msg));
2705 }
2706 for r in &a.representations {
2707 for bu in &r.BaseURL {
2708 if !valid_url_p(&bu.base) {
2709 errors.push(format!("invalid URL {}", &bu.base));
2710 }
2711 }
2712 if let Some(sb) = &r.SegmentBase {
2713 if let Some(init) = &sb.Initialization {
2714 if let Some(su) = &init.sourceURL {
2715 if !valid_url_p(su) {
2716 errors.push(format!("invalid URL {su}"));
2717 }
2718 if su.contains("$Number") {
2719 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2720 }
2721 if su.contains("$Time") {
2722 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2723 }
2724 }
2725 }
2726 if let Some(ri) = &sb.representation_index {
2727 if let Some(su) = &ri.sourceURL {
2728 if !valid_url_p(su) {
2729 errors.push(format!("invalid URL {su}"));
2730 }
2731 }
2732 }
2733 }
2734 if let Some(sl) = &r.SegmentList {
2735 if let Some(hr) = &sl.href {
2736 if !valid_url_p(hr) {
2737 errors.push(format!("invalid URL {hr}"));
2738 }
2739 }
2740 if let Some(init) = &sl.Initialization {
2741 if let Some(su) = &init.sourceURL {
2742 if !valid_url_p(su) {
2743 errors.push(format!("invalid URL {su}"));
2744 }
2745 if su.contains("$Number") {
2746 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2747 }
2748 if su.contains("$Time") {
2749 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2750 }
2751 }
2752 }
2753 for su in &sl.segment_urls {
2754 if let Some(md) = &su.media {
2755 if !valid_url_p(md) {
2756 errors.push(format!("invalid URL {md}"));
2757 }
2758 }
2759 if let Some(ix) = &su.index {
2760 if !valid_url_p(ix) {
2761 errors.push(format!("invalid URL {ix}"));
2762 }
2763 }
2764 }
2765 }
2766 if let Some(st) = &r.SegmentTemplate {
2767 check_segment_template_conformity(st)
2768 .into_iter()
2769 .for_each(|msg| errors.push(msg));
2770 }
2771 }
2772 }
2773 }
2774 for pi in &mpd.ProgramInformation {
2775 if let Some(u) = &pi.moreInformationURL {
2776 if !valid_url_p(u) {
2777 errors.push(format!("invalid URL {u}"));
2778 }
2779 }
2780 }
2781 errors
2782}
2783
2784#[cfg(test)]
2785mod tests {
2786 use proptest::prelude::*;
2787 use std::fs;
2788 use std::path::PathBuf;
2789 use std::time::Duration;
2790
2791 proptest! {
2792 #[test]
2793 fn doesnt_crash(s in "\\PC*") {
2794 let _ = super::parse_xs_duration(&s);
2795 let _ = super::parse_xs_datetime(&s);
2796 }
2797 }
2798
2799 #[test]
2800 fn test_parse_xs_duration() {
2801 use super::parse_xs_duration;
2802
2803 assert!(parse_xs_duration("").is_err());
2804 assert!(parse_xs_duration("foobles").is_err());
2805 assert!(parse_xs_duration("P").is_err());
2806 assert!(parse_xs_duration("PW").is_err());
2807 assert!(parse_xs_duration("-PT4.5S").is_err());
2809 assert!(parse_xs_duration("1Y2M3DT4H5M6S").is_err()); assert_eq!(parse_xs_duration("PT3H11M53S").ok(), Some(Duration::new(11513, 0)));
2811 assert_eq!(parse_xs_duration("PT42M30S").ok(), Some(Duration::new(2550, 0)));
2812 assert_eq!(parse_xs_duration("PT30M38S").ok(), Some(Duration::new(1838, 0)));
2813 assert_eq!(parse_xs_duration("PT0H10M0.00S").ok(), Some(Duration::new(600, 0)));
2814 assert_eq!(parse_xs_duration("PT1.5S").ok(), Some(Duration::new(1, 500_000_000)));
2815 assert_eq!(parse_xs_duration("PT1.500S").ok(), Some(Duration::new(1, 500_000_000)));
2816 assert_eq!(parse_xs_duration("PT1.500000000S").ok(), Some(Duration::new(1, 500_000_000)));
2817 assert_eq!(parse_xs_duration("PT0S").ok(), Some(Duration::new(0, 0)));
2818 assert_eq!(parse_xs_duration("PT0.001S").ok(), Some(Duration::new(0, 1_000_000)));
2819 assert_eq!(parse_xs_duration("PT0.00100S").ok(), Some(Duration::new(0, 1_000_000)));
2820 assert_eq!(parse_xs_duration("PT344S").ok(), Some(Duration::new(344, 0)));
2821 assert_eq!(parse_xs_duration("PT634.566S").ok(), Some(Duration::new(634, 566_000_000)));
2822 assert_eq!(parse_xs_duration("PT72H").ok(), Some(Duration::new(72*60*60, 0)));
2823 assert_eq!(parse_xs_duration("PT0H0M30.030S").ok(), Some(Duration::new(30, 30_000_000)));
2824 assert_eq!(parse_xs_duration("PT1004199059S").ok(), Some(Duration::new(1004199059, 0)));
2825 assert_eq!(parse_xs_duration("P0Y20M0D").ok(), Some(Duration::new(51840000, 0)));
2826 assert_eq!(parse_xs_duration("PT1M30.5S").ok(), Some(Duration::new(90, 500_000_000)));
2827 assert_eq!(parse_xs_duration("PT10M10S").ok(), Some(Duration::new(610, 0)));
2828 assert_eq!(parse_xs_duration("PT1H0.040S").ok(), Some(Duration::new(3600, 40_000_000)));
2829 assert_eq!(parse_xs_duration("PT00H03M30SZ").ok(), Some(Duration::new(210, 0)));
2830 assert_eq!(parse_xs_duration("PT3.14159S").ok(), Some(Duration::new(3, 141_590_000)));
2831 assert_eq!(parse_xs_duration("PT3.14159265S").ok(), Some(Duration::new(3, 141_592_650)));
2832 assert_eq!(parse_xs_duration("PT3.141592653S").ok(), Some(Duration::new(3, 141_592_653)));
2833 assert_eq!(parse_xs_duration("PT3.141592653897S").ok(), Some(Duration::new(3, 141_592_653)));
2835 assert_eq!(parse_xs_duration("P0W").ok(), Some(Duration::new(0, 0)));
2836 assert_eq!(parse_xs_duration("P26W").ok(), Some(Duration::new(15724800, 0)));
2837 assert_eq!(parse_xs_duration("P52W").ok(), Some(Duration::new(31449600, 0)));
2838 assert_eq!(parse_xs_duration("P10D").ok(), Some(Duration::new(864000, 0)));
2839 assert_eq!(parse_xs_duration("P0Y").ok(), Some(Duration::new(0, 0)));
2840 assert_eq!(parse_xs_duration("P1Y").ok(), Some(Duration::new(31536000, 0)));
2841 assert_eq!(parse_xs_duration("P1Y0W0S").ok(), Some(Duration::new(31536000, 0)));
2842 assert_eq!(parse_xs_duration("PT4H").ok(), Some(Duration::new(14400, 0)));
2843 assert_eq!(parse_xs_duration("+PT4H").ok(), Some(Duration::new(14400, 0)));
2844 assert_eq!(parse_xs_duration("PT0004H").ok(), Some(Duration::new(14400, 0)));
2845 assert_eq!(parse_xs_duration("PT4H0M").ok(), Some(Duration::new(14400, 0)));
2846 assert_eq!(parse_xs_duration("PT4H0S").ok(), Some(Duration::new(14400, 0)));
2847 assert_eq!(parse_xs_duration("P23DT23H").ok(), Some(Duration::new(2070000, 0)));
2848 assert_eq!(parse_xs_duration("P0Y0M0DT0H4M20.880S").ok(), Some(Duration::new(260, 880_000_000)));
2849 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6.7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2850 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6,7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2851
2852 }
2856
2857 #[test]
2858 fn test_serialize_xs_duration() {
2859 use super::MPD;
2860
2861 fn serialized_xs_duration(d: Duration) -> String {
2862 let mpd = MPD {
2863 minBufferTime: Some(d),
2864 ..Default::default()
2865 };
2866 let xml = mpd.to_string();
2867 let doc = roxmltree::Document::parse(&xml).unwrap();
2868 String::from(doc.root_element().attribute("minBufferTime").unwrap())
2869 }
2870
2871 assert_eq!("PT0S", serialized_xs_duration(Duration::new(0, 0)));
2872 assert_eq!("PT0.001S", serialized_xs_duration(Duration::new(0, 1_000_000)));
2873 assert_eq!("PT42S", serialized_xs_duration(Duration::new(42, 0)));
2874 assert_eq!("PT1.5S", serialized_xs_duration(Duration::new(1, 500_000_000)));
2875 assert_eq!("PT30.03S", serialized_xs_duration(Duration::new(30, 30_000_000)));
2876 assert_eq!("PT1M30.5S", serialized_xs_duration(Duration::new(90, 500_000_000)));
2877 assert_eq!("PT5M44S", serialized_xs_duration(Duration::new(344, 0)));
2878 assert_eq!("PT42M30S", serialized_xs_duration(Duration::new(2550, 0)));
2879 assert_eq!("PT30M38S", serialized_xs_duration(Duration::new(1838, 0)));
2880 assert_eq!("PT10M10S", serialized_xs_duration(Duration::new(610, 0)));
2881 assert_eq!("PT1H0M0.04S", serialized_xs_duration(Duration::new(3600, 40_000_000)));
2882 assert_eq!("PT3H11M53S", serialized_xs_duration(Duration::new(11513, 0)));
2883 assert_eq!("PT4H0M0S", serialized_xs_duration(Duration::new(14400, 0)));
2884 }
2885
2886 #[test]
2887 fn test_parse_xs_datetime() {
2888 use chrono::{DateTime, NaiveDate};
2889 use chrono::offset::Utc;
2890 use super::parse_xs_datetime;
2891
2892 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2893 .unwrap()
2894 .and_hms_opt(1, 3, 2)
2895 .unwrap();
2896 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02Z").ok(),
2897 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2898 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2899 .unwrap()
2900 .and_hms_nano_opt(1, 3, 2, 958*1000*1000)
2901 .unwrap();
2902 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02.958Z").ok(),
2903 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2904 }
2905
2906 #[test]
2907 fn test_parse_failure() {
2908 use super::parse;
2909
2910 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2911 path.push("tests");
2912 path.push("fixtures");
2913 path.push("incomplete.mpd");
2914 let xml = fs::read_to_string(path).unwrap();
2915 assert!(matches!(parse(&xml), Err(crate::DashMpdError::Parsing(_))));
2916 }
2917
2918 #[test]
2919 fn test_conformity_checking() {
2920 use super::{parse, check_conformity};
2921
2922 for fixture in [
2924 "a2d-tv.mpd",
2925 "ad-insertion-testcase1.mpd",
2926 "ad-insertion-testcase6-av1.mpd",
2927 "ad-insertion-testcase6-av2.mpd",
2928 "ad-insertion-testcase6-av5.mpd",
2929 "aws.xml",
2930 "dashif-live-atoinf.mpd",
2931 "dashif-low-latency.mpd",
2932 "dash-testcases-5b-1-thomson.mpd",
2933 "dolby-ac4.xml",
2934 "example_G22.mpd",
2935 "f64-inf.mpd",
2936 "jurassic-compact-5975.mpd",
2937 "mediapackage.xml",
2938 "multiple_supplementals.mpd",
2939 "orange.xml",
2940 "patch-location.mpd",
2941 "st-sl.mpd",
2942 "telenet-mid-ad-rolls.mpd",
2943 "manifest_wvcenc_1080p.mpd"] {
2944 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2945 path.push("tests");
2946 path.push("fixtures");
2947 path.push(fixture);
2948 let xml = fs::read_to_string(path)
2949 .unwrap_or_else(|_| panic!("failed to read fixture {fixture}"));
2950 let mpd = parse(&xml)
2951 .unwrap_or_else(|_| panic!("failed to parse fixture {fixture}"));
2952 let anomalies = check_conformity(&mpd);
2953 assert!(anomalies.is_empty());
2954 }
2955 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2957 path.push("tests");
2958 path.push("fixtures");
2959 path.push("admanager.xml");
2960 let xml = fs::read_to_string(path).unwrap();
2961 let mpd = parse(&xml).unwrap();
2962 let anomalies = check_conformity(&mpd);
2963 assert!(!anomalies.is_empty());
2964 for anomaly in anomalies {
2965 assert!(anomaly.starts_with("SegmentTimeline has segment@d"));
2966 }
2967 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2968 path.push("tests");
2969 path.push("fixtures");
2970 path.push( "avod-mediatailor.mpd");
2971 let xml = fs::read_to_string(path).unwrap();
2972 let mpd = parse(&xml).unwrap();
2973 let anomalies = check_conformity(&mpd);
2974 assert!(!anomalies.is_empty());
2975 for anomaly in anomalies {
2976 assert!(anomaly.starts_with("SegmentTimeline has segment@d"));
2977 }
2978 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2979 path.push("tests");
2980 path.push("fixtures");
2981 path.push("telestream-binary.xml");
2982 let xml = fs::read_to_string(path).unwrap();
2983 let mpd = parse(&xml).unwrap();
2984 let anomalies = check_conformity(&mpd);
2985 assert!(!anomalies.is_empty());
2986 for anomaly in anomalies {
2987 assert!(anomaly.starts_with("Period with @id <unspecified> contains no AdaptationSet elements"));
2988 }
2989 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2990 path.push("tests");
2991 path.push("fixtures");
2992 path.push("telestream-elements.xml");
2993 let xml = fs::read_to_string(path).unwrap();
2994 let mpd = parse(&xml).unwrap();
2995 let anomalies = check_conformity(&mpd);
2996 assert!(!anomalies.is_empty());
2997 for anomaly in anomalies {
2998 assert!(anomaly.starts_with("Period with @id <unspecified> contains no AdaptationSet elements"));
2999 }
3000 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3001 path.push("tests");
3002 path.push("fixtures");
3003 path.push("vod-aip-unif-streaming.mpd");
3004 let xml = fs::read_to_string(path).unwrap();
3005 let mpd = parse(&xml).unwrap();
3006 let anomalies = check_conformity(&mpd);
3007 assert!(!anomalies.is_empty());
3008 for anomaly in anomalies {
3009 assert!(anomaly.starts_with("SegmentTimeline has segment@d > @maxSegmentDuration"));
3010 }
3011 }
3012}