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> {
213 use std::cmp::min;
214
215 match XS_DURATION_REGEX.captures(s) {
216 Some(m) => {
217 if m.name("hastime").is_none() &&
218 m.name("years").is_none() &&
219 m.name("months").is_none() &&
220 m.name("weeks").is_none() &&
221 m.name("days").is_none() {
222 return Err(DashMpdError::InvalidDuration("empty".to_string()));
223 }
224 let mut secs: u64 = 0;
225 let mut nsecs: u32 = 0;
226 if let Some(nano) = m.name("nanoseconds") {
227 let lim = min(nano.as_str().len(), 9 + ".".len());
230 if let Some(ss) = &nano.as_str().get(1..lim) {
231 let padded = format!("{ss:0<9}");
232 nsecs = padded.parse::<u32>()
233 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
234 }
235 }
236 if let Some(mseconds) = m.name("seconds") {
237 let seconds = mseconds.as_str().parse::<u64>()
238 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
239 secs += seconds;
240 }
241 if let Some(mminutes) = m.name("minutes") {
242 let minutes = mminutes.as_str().parse::<u64>()
243 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
244 secs += minutes * 60;
245 }
246 if let Some(mhours) = m.name("hours") {
247 let hours = mhours.as_str().parse::<u64>()
248 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
249 secs += hours * 60 * 60;
250 }
251 if let Some(mdays) = m.name("days") {
252 let days = mdays.as_str().parse::<u64>()
253 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
254 secs += days * 60 * 60 * 24;
255 }
256 if let Some(mweeks) = m.name("weeks") {
257 let weeks = mweeks.as_str().parse::<u64>()
258 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
259 secs += weeks * 60 * 60 * 24 * 7;
260 }
261 if let Some(mmonths) = m.name("months") {
262 let months = mmonths.as_str().parse::<u64>()
263 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
264 secs += months * 60 * 60 * 24 * 30;
265 }
266 if let Some(myears) = m.name("years") {
267 let years = myears.as_str().parse::<u64>()
268 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
269 secs += years * 60 * 60 * 24 * 365;
270 }
271 if let Some(msign) = m.name("sign") {
272 if msign.as_str() == "-" {
273 return Err(DashMpdError::InvalidDuration("can't represent negative durations".to_string()));
274 }
275 }
276 Ok(Duration::new(secs, nsecs))
277 },
278 None => Err(DashMpdError::InvalidDuration(String::from("couldn't parse XS duration"))),
279 }
280}
281
282
283fn deserialize_xs_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
331where
332 D: de::Deserializer<'de>,
333{
334 match <Option<String>>::deserialize(deserializer) {
335 Ok(optstring) => match optstring {
336 Some(xs) => match parse_xs_duration(&xs) {
337 Ok(d) => Ok(Some(d)),
338 Err(e) => Err(de::Error::custom(e)),
339 },
340 None => Ok(None),
341 },
342 Err(_) => Ok(None),
344 }
345}
346
347fn serialize_xs_duration<S>(oxs: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
355where
356 S: Serializer,
357{
358 if let Some(xs) = oxs {
359 let total_secs = xs.as_secs();
360 let nanos = xs.subsec_nanos();
361 let hours = total_secs / 3600;
362 let mins = (total_secs % 3600) / 60;
363 let secs = total_secs % 60;
364 let frac = if nanos > 0 {
365 format!(".{nanos:09}").trim_end_matches('0').to_string()
366 } else {
367 String::new()
368 };
369 let s = match (hours, mins, secs, nanos) {
370 (h, 0, 0, 0) if h > 0 => format!("PT{h}H"),
371 (h, m, 0, 0) if h > 0 => format!("PT{h}H{m}M"),
372 (0, m, 0, 0) if m > 0 => format!("PT{m}M"),
373 (h, m, s, _) if h > 0 => format!("PT{h}H{m}M{s}{frac}S"),
374 (0, m, s, _) if m > 0 => format!("PT{m}M{s}{frac}S"),
375 _ => format!("PT{secs}{frac}S"),
376 };
377 serializer.serialize_str(&s)
378 } else {
379 serializer.serialize_none()
381 }
382}
383
384
385fn parse_xs_datetime(s: &str) -> Result<XsDatetime, DashMpdError> {
391 use iso8601::Date;
392 use chrono::{LocalResult, NaiveDate, TimeZone};
393 use num_traits::cast::FromPrimitive;
394 match DateTime::<chrono::offset::FixedOffset>::parse_from_rfc3339(s) {
395 Ok(dt) => Ok(dt.into()),
396 Err(_) => match iso8601::datetime(s) {
397 Ok(dt) => {
398 let nd = match dt.date {
399 Date::YMD { year, month, day } =>
400 NaiveDate::from_ymd_opt(year, month, day)
401 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
402 Date::Week { year, ww, d } => {
403 let d = chrono::Weekday::from_u32(d)
404 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
405 NaiveDate::from_isoywd_opt(year, ww, d)
406 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
407 },
408 Date::Ordinal { year, ddd } =>
409 NaiveDate::from_yo_opt(year, ddd)
410 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
411 };
412 let nd = nd.and_hms_nano_opt(dt.time.hour, dt.time.minute, dt.time.second, dt.time.millisecond*1000*1000)
413 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
414 let tz_secs = dt.time.tz_offset_hours * 3600 + dt.time.tz_offset_minutes * 60;
415 match chrono::FixedOffset::east_opt(tz_secs)
416 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
417 .from_local_datetime(&nd)
418 {
419 LocalResult::Single(local) => Ok(local.with_timezone(&chrono::Utc)),
420 _ => Err(DashMpdError::InvalidDateTime(s.to_string())),
421 }
422 },
423 Err(_) => Err(DashMpdError::InvalidDateTime(s.to_string())),
424 }
425 }
426}
427
428fn deserialize_xs_datetime<'de, D>(deserializer: D) -> Result<Option<XsDatetime>, D::Error>
430where
431 D: de::Deserializer<'de>,
432{
433 match <Option<String>>::deserialize(deserializer) {
434 Ok(optstring) => match optstring {
435 Some(xs) => match parse_xs_datetime(&xs) {
436 Ok(d) => Ok(Some(d)),
437 Err(e) => Err(de::Error::custom(e)),
438 },
439 None => Ok(None),
440 },
441 Err(_) => Ok(None),
443 }
444}
445
446fn serialize_xsd_uintvector<S>(v: &Vec<u64>, serializer: S) -> Result<S::Ok, S::Error>
449where
450 S: Serializer,
451{
452 let mut formatted = String::new();
453 for u in v {
454 formatted += &format!("{u} ");
455 }
456 serializer.serialize_str(&formatted)
457}
458
459fn deserialize_xsd_uintvector<'de, D>(deserializer: D) -> Result<Vec<u64>, D::Error>
460where
461 D: de::Deserializer<'de>,
462{
463 let s = String::deserialize(deserializer)?;
464 let mut out = Vec::<u64>::new();
465 for uint64_str in s.split_whitespace() {
466 match uint64_str.parse::<u64>() {
467 Ok(val) => out.push(val),
468 Err(e) => return Err(de::Error::custom(e)),
469 }
470 }
471 Ok(out)
472}
473
474fn serialize_xmlns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
484where S: serde::Serializer {
485 if let Some(s) = os {
486 serializer.serialize_str(s)
487 } else {
488 serializer.serialize_str("urn:mpeg:dash:schema:mpd:2011")
489 }
490}
491
492fn serialize_xsi_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
493where S: serde::Serializer {
494 if let Some(s) = os {
495 serializer.serialize_str(s)
496 } else {
497 serializer.serialize_str("http://www.w3.org/2001/XMLSchema-instance")
498 }
499}
500
501fn serialize_cenc_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
502where S: serde::Serializer {
503 if let Some(s) = os {
504 serializer.serialize_str(s)
505 } else {
506 serializer.serialize_str("urn:mpeg:cenc:2013")
507 }
508}
509
510fn serialize_mspr_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
511where S: serde::Serializer {
512 if let Some(s) = os {
513 serializer.serialize_str(s)
514 } else {
515 serializer.serialize_str("urn:microsoft:playready")
516 }
517}
518
519fn serialize_xlink_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
520where S: serde::Serializer {
521 if let Some(s) = os {
522 serializer.serialize_str(s)
523 } else {
524 serializer.serialize_str("http://www.w3.org/1999/xlink")
525 }
526}
527
528fn serialize_dvb_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
529where S: serde::Serializer {
530 if let Some(s) = os {
531 serializer.serialize_str(s)
532 } else {
533 serializer.serialize_str("urn:dvb:dash-extensions:2014-1")
534 }
535}
536
537
538fn default_optstring_on_request() -> Option<String> {
542 Some("onRequest".to_string())
543}
544
545fn default_optstring_one() -> Option<String> {
546 Some(String::from("1"))
547}
548
549fn default_optstring_encoder() -> Option<String> {
550 Some(String::from("encoder"))
551}
552
553fn default_optstring_any() -> Option<String> {
554 Some(String::from("any"))
555}
556
557fn default_optstring_query() -> Option<String> {
558 Some(String::from("query"))
559}
560
561fn default_optstring_segment() -> Option<String> {
562 Some(String::from("segment"))
563}
564
565fn default_optbool_true() -> Option<bool> {
566 Some(true)
567}
568
569fn default_optbool_false() -> Option<bool> {
570 Some(false)
571}
572
573fn default_optu64_zero() -> Option<u64> {
574 Some(0)
575}
576
577fn default_optu64_one() -> Option<u64> {
578 Some(1)
579}
580
581
582#[skip_serializing_none]
595#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
596#[serde(default)]
597pub struct Title {
598 #[serde(rename = "$text")]
599 pub content: Option<String>,
600}
601
602#[skip_serializing_none]
604#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
605#[serde(default)]
606pub struct Source {
607 #[serde(rename = "$text")]
608 pub content: Option<String>,
609}
610
611#[skip_serializing_none]
613#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
614#[serde(default)]
615pub struct Copyright {
616 #[serde(rename = "$text")]
617 pub content: Option<String>,
618}
619
620#[skip_serializing_none]
622#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
623#[serde(default)]
624pub struct ProgramInformation {
625 #[serde(rename = "@lang")]
627 pub lang: Option<String>,
628 #[serde(rename = "@moreInformationURL")]
629 pub moreInformationURL: Option<String>,
630 pub Title: Option<Title>,
631 pub Source: Option<Source>,
632 pub Copyright: Option<Copyright>,
633 #[serde(rename(serialize = "scte214:ContentIdentifier", deserialize = "ContentIdentifier"))]
634 pub scte214ContentIdentifier: Option<Scte214ContentIdentifier>,
635}
636
637#[skip_serializing_none]
641#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
642#[serde(default)]
643pub struct Scte214ContentIdentifier {
644 #[serde(rename = "@type")]
645 pub idType: Option<String>,
646 #[serde(rename = "@value")]
647 pub idValue: Option<String>,
648}
649
650#[skip_serializing_none]
652#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
653#[serde(default)]
654pub struct S {
655 #[serde(rename = "@t")]
657 pub t: Option<u64>,
658 #[serde(rename = "@n")]
659 pub n: Option<u64>,
660 #[serde(rename = "@d")]
662 pub d: u64,
663 #[serde(rename = "@r")]
666 pub r: Option<i64>,
667 #[serde(rename = "@k")]
668 pub k: Option<u64>,
669}
670
671#[skip_serializing_none]
674#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
675#[serde(default)]
676pub struct SegmentTimeline {
677 #[serde(rename = "S")]
679 pub segments: Vec<S>,
680}
681
682#[skip_serializing_none]
689#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
690#[serde(default)]
691pub struct BitstreamSwitching {
692 #[serde(rename = "@sourceURL")]
693 pub source_url: Option<String>,
694 #[serde(rename = "@range")]
695 pub range: Option<String>,
696}
697
698#[skip_serializing_none]
702#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
703#[serde(default)]
704pub struct Initialization {
705 #[serde(rename = "@sourceURL")]
706 pub sourceURL: Option<String>,
707 #[serde(rename = "@range")]
708 pub range: Option<String>,
709}
710
711#[skip_serializing_none]
712#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
713#[serde(default)]
714pub struct RepresentationIndex {
715 #[serde(rename = "@range")]
716 pub range: Option<String>,
717 #[serde(rename = "@sourceURL")]
718 pub sourceURL: Option<String>,
719}
720
721#[skip_serializing_none]
724#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
725#[serde(default)]
726pub struct SegmentTemplate {
727 #[serde(rename = "@media")]
728 pub media: Option<String>,
729 #[serde(rename = "@index")]
730 pub index: Option<String>,
731 #[serde(rename = "@initialization")]
732 pub initialization: Option<String>,
733 #[serde(rename = "@bitstreamSwitching")]
734 pub bitstreamSwitching: Option<String>,
735 #[serde(rename = "@indexRange")]
736 pub indexRange: Option<String>,
737 #[serde(rename = "@indexRangeExact")]
738 pub indexRangeExact: Option<bool>,
739 #[serde(rename = "@startNumber")]
740 pub startNumber: Option<u64>,
741 #[serde(rename = "@duration")]
745 pub duration: Option<f64>,
746 #[serde(rename = "@timescale")]
747 pub timescale: Option<u64>,
748 #[serde(rename = "@eptDelta")]
750 pub eptDelta: Option<i64>,
751 #[serde(rename = "@pdDelta")]
754 pub pbDelta: Option<i64>,
755 #[serde(rename = "@presentationTimeOffset")]
756 pub presentationTimeOffset: Option<u64>,
757 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
758 pub availabilityTimeOffset: Option<f64>,
759 #[serde(rename = "@availabilityTimeComplete")]
760 pub availabilityTimeComplete: Option<bool>,
761 pub Initialization: Option<Initialization>,
762 #[serde(rename = "RepresentationIndex")]
763 pub representation_index: Option<RepresentationIndex>,
764 #[serde(rename = "FailoverContent")]
768 pub failover_content: Option<FailoverContent>,
769 pub SegmentTimeline: Option<SegmentTimeline>,
770 pub BitstreamSwitching: Option<BitstreamSwitching>,
771}
772
773#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
777#[serde(default)]
778pub struct Location {
779 #[serde(rename = "$text")]
780 pub url: String,
781}
782
783#[skip_serializing_none]
789#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
790#[serde(default)]
791pub struct BaseURL {
792 #[serde(rename = "@serviceLocation")]
793 pub serviceLocation: Option<String>,
794 #[serde(rename = "@byteRange")]
795 pub byte_range: Option<String>,
796 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
799 pub availability_time_offset: Option<f64>,
800 #[serde(rename = "@availabilityTimeComplete")]
801 pub availability_time_complete: Option<bool>,
802 #[serde(rename = "@timeShiftBufferDepth",
803 serialize_with = "serialize_xs_duration",
804 deserialize_with = "deserialize_xs_duration",
805 default)]
806 pub timeShiftBufferDepth: Option<Duration>,
807 #[serde(rename = "@dvb:priority", alias = "@priority")]
809 pub priority: Option<u64>,
810 #[serde(rename = "@dvb:weight", alias = "@weight")]
814 pub weight: Option<i64>,
815 #[serde(rename = "$text")]
816 pub base: String,
817}
818
819#[skip_serializing_none]
825#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
826#[serde(default)]
827pub struct Fcs {
828 #[serde(rename = "@t")]
831 pub t: u64,
832
833 #[serde(rename = "@d")]
836 pub d: Option<u64>,
837}
838
839#[skip_serializing_none]
842#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
843#[serde(default)]
844pub struct FailoverContent {
845 #[serde(rename = "@valid")]
848 pub valid: Option<bool>,
849 #[serde(rename = "FCS")]
850 pub fcs_list: Vec<Fcs>,
851}
852
853#[skip_serializing_none]
855#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
856#[serde(default)]
857pub struct SegmentBase {
858 #[serde(rename = "@timescale")]
859 pub timescale: Option<u64>,
860 #[serde(rename = "@presentationTimeOffset")]
861 pub presentationTimeOffset: Option<u64>,
862 #[serde(rename = "@indexRange")]
863 pub indexRange: Option<String>,
864 #[serde(rename = "@indexRangeExact")]
865 pub indexRangeExact: Option<bool>,
866 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
867 pub availabilityTimeOffset: Option<f64>,
868 #[serde(rename = "@availabilityTimeComplete")]
869 pub availabilityTimeComplete: Option<bool>,
870 #[serde(rename = "@presentationDuration")]
871 pub presentationDuration: Option<u64>,
872 #[serde(rename = "@eptDelta")]
874 pub eptDelta: Option<i64>,
875 #[serde(rename = "@pdDelta")]
878 pub pbDelta: Option<i64>,
879 pub Initialization: Option<Initialization>,
880 #[serde(rename = "RepresentationIndex")]
881 pub representation_index: Option<RepresentationIndex>,
882 #[serde(rename = "FailoverContent")]
883 pub failover_content: Option<FailoverContent>,
884}
885
886#[skip_serializing_none]
888#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
889#[serde(default)]
890pub struct SegmentURL {
891 #[serde(rename = "@media")]
892 pub media: Option<String>, #[serde(rename = "@mediaRange")]
894 pub mediaRange: Option<String>,
895 #[serde(rename = "@index")]
896 pub index: Option<String>, #[serde(rename = "@indexRange")]
898 pub indexRange: Option<String>,
899}
900
901#[skip_serializing_none]
903#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
904#[serde(default)]
905pub struct SegmentList {
906 #[serde(rename = "@duration")]
908 pub duration: Option<u64>,
909 #[serde(rename = "@timescale")]
910 pub timescale: Option<u64>,
911 #[serde(rename = "@indexRange")]
912 pub indexRange: Option<String>,
913 #[serde(rename = "@indexRangeExact")]
914 pub indexRangeExact: Option<bool>,
915 #[serde(rename = "@xlink:href", alias = "@href")]
917 pub href: Option<String>,
918 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
919 pub actuate: Option<String>,
920 #[serde(rename = "@xlink:type", alias = "@type")]
921 pub sltype: Option<String>,
922 #[serde(rename = "@xlink:show", alias = "@show")]
923 pub show: Option<String>,
924 pub Initialization: Option<Initialization>,
925 pub SegmentTimeline: Option<SegmentTimeline>,
926 pub BitstreamSwitching: Option<BitstreamSwitching>,
927 #[serde(rename = "SegmentURL")]
928 pub segment_urls: Vec<SegmentURL>,
929}
930
931#[skip_serializing_none]
932#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
933#[serde(default)]
934pub struct Resync {
935 #[serde(rename = "@type")]
936 pub rtype: Option<String>,
937 #[serde(rename = "@dT")]
938 pub dT: Option<u64>,
939 #[serde(rename = "@dImax")]
940 pub dImax: Option<f64>,
941 #[serde(rename = "@dImin")]
942 pub dImin: Option<f64>,
943 #[serde(rename = "@marker")]
944 pub marker: Option<bool>,
945}
946
947#[skip_serializing_none]
949#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
950#[serde(default)]
951pub struct AudioChannelConfiguration {
952 #[serde(rename = "@schemeIdUri")]
953 pub schemeIdUri: String,
954 #[serde(rename = "@value")]
955 pub value: Option<String>,
956 #[serde(rename = "@id")]
957 pub id: Option<String>,
958}
959
960#[skip_serializing_none]
962#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
963#[serde(default)]
964pub struct Language {
965 #[serde(rename = "$text")]
966 pub content: Option<String>,
967}
968
969#[skip_serializing_none]
975#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
976#[serde(default)]
977pub struct Preselection {
978 #[serde(rename = "@id", default = "default_optstring_one")]
979 pub id: Option<String>,
980 #[serde(rename = "@preselectionComponents")]
983 pub preselectionComponents: String,
984 #[serde(rename = "@lang")]
985 pub lang: Option<String>,
986 #[serde(rename = "@audioSamplingRate")]
987 pub audioSamplingRate: Option<String>,
988 #[serde(rename = "@codecs")]
990 pub codecs: String,
991 #[serde(rename = "@selectionPriority")]
992 pub selectionPriority: Option<u64>,
993 #[serde(rename = "@tag")]
994 pub tag: String,
995 pub FramePacking: Vec<FramePacking>,
996 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
997 pub ContentProtection: Vec<ContentProtection>,
998 pub OutputProtection: Option<OutputProtection>,
999 #[serde(rename = "EssentialProperty")]
1000 pub essential_property: Vec<EssentialProperty>,
1001 #[serde(rename = "SupplementalProperty")]
1002 pub supplemental_property: Vec<SupplementalProperty>,
1003 pub InbandEventStream: Vec<InbandEventStream>,
1004 pub Switching: Vec<Switching>,
1005 #[serde(rename = "GroupLabel")]
1007 pub group_label: Vec<Label>,
1008 pub Label: Vec<Label>,
1009 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
1010 pub Resync: Option<Resync>,
1012 #[serde(rename = "Accessibility")]
1013 pub accessibilities: Vec<Accessibility>,
1014 #[serde(rename = "Role")]
1015 pub roles: Vec<Role>,
1016 #[serde(rename = "Rating")]
1017 pub ratings: Vec<Rating>,
1018 #[serde(rename = "Viewpoint")]
1019 pub viewpoints: Vec<Viewpoint>,
1020 #[serde(rename = "Language")]
1022 pub languages: Vec<Language>,
1023}
1024
1025#[skip_serializing_none]
1028#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1029#[serde(default)]
1030pub struct Rating {
1031 #[serde(rename = "@id")]
1032 pub id: Option<String>,
1033 #[serde(rename = "@schemeIdUri")]
1034 pub schemeIdUri: String,
1035 #[serde(rename = "@value")]
1036 pub value: Option<String>,
1037}
1038
1039#[skip_serializing_none]
1041#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1042#[serde(default)]
1043pub struct FramePacking {
1044 #[serde(rename = "@id")]
1045 pub id: Option<String>,
1046 #[serde(rename = "@schemeIdUri")]
1047 pub schemeIdUri: String,
1048 #[serde(rename = "@value")]
1049 pub value: Option<String>,
1050}
1051
1052#[skip_serializing_none]
1057#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1058#[serde(default)]
1059pub struct Switching {
1060 #[serde(rename = "@interval")]
1061 pub interval: Option<u64>,
1062 #[serde(rename = "@type")]
1064 pub stype: Option<String>,
1065}
1066
1067#[skip_serializing_none]
1069#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1070#[serde(default)]
1071pub struct Accessibility {
1072 #[serde(rename = "@schemeIdUri")]
1073 pub schemeIdUri: String,
1074 #[serde(rename = "@value")]
1075 pub value: Option<String>,
1076 #[serde(rename = "@id")]
1077 pub id: Option<String>,
1078}
1079
1080#[skip_serializing_none]
1082#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1083#[serde(default)]
1084pub struct Scope {
1085 #[serde(rename = "@schemeIdUri")]
1086 pub schemeIdUri: String,
1087 #[serde(rename = "@value")]
1088 pub value: Option<String>,
1089 #[serde(rename = "@id")]
1090 pub id: Option<String>,
1091}
1092
1093#[skip_serializing_none]
1095#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1096#[serde(default)]
1097pub struct SubRepresentation {
1098 #[serde(rename = "@level")]
1099 pub level: Option<u32>,
1100 #[serde(rename = "@dependencyLevel")]
1101 pub dependencyLevel: Option<String>,
1102 #[serde(rename = "@contentComponent")]
1104 pub contentComponent: Option<String>,
1105 #[serde(rename = "@mimeType")]
1106 pub mimeType: Option<String>,
1107 #[serde(rename = "@codecs")]
1109 pub codecs: Option<String>,
1110 #[serde(rename = "@contentType")]
1111 pub contentType: Option<String>,
1112 #[serde(rename = "@profiles")]
1113 pub profiles: Option<String>,
1114 #[serde(rename = "@segmentProfiles")]
1115 pub segmentProfiles: Option<String>,
1118 #[serde(rename = "@scanType")]
1120 pub scanType: Option<String>,
1121 #[serde(rename = "@frameRate")]
1122 pub frameRate: Option<String>, #[serde(rename = "@sar")]
1125 pub sar: Option<String>,
1126 #[serde(rename = "@bandwidth")]
1128 pub bandwidth: Option<u64>,
1129 #[serde(rename = "@audioSamplingRate")]
1130 pub audioSamplingRate: Option<String>,
1131 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1133 pub maxPlayoutRate: Option<f64>,
1134 #[serde(rename = "@codingDependency")]
1135 pub codingDependency: Option<bool>,
1136 #[serde(rename = "@width")]
1137 pub width: Option<u64>,
1138 #[serde(rename = "@height")]
1139 pub height: Option<u64>,
1140 #[serde(rename = "@startWithSAP")]
1141 pub startWithSAP: Option<u64>,
1142 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1143 pub maximumSAPPeriod: Option<f64>,
1144 pub FramePacking: Vec<FramePacking>,
1145 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1146 pub ContentProtection: Vec<ContentProtection>,
1147 pub OutputProtection: Option<OutputProtection>,
1148 #[serde(rename = "EssentialProperty")]
1149 pub essential_property: Vec<EssentialProperty>,
1150 #[serde(rename = "SupplementalProperty")]
1151 pub supplemental_property: Vec<SupplementalProperty>,
1152 pub InbandEventStream: Vec<InbandEventStream>,
1153 pub Switching: Vec<Switching>,
1154 #[serde(rename = "GroupLabel")]
1156 pub group_label: Vec<Label>,
1157 pub Label: Vec<Label>,
1158 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
1159 pub Resync: Option<Resync>,
1161}
1162
1163#[skip_serializing_none]
1168#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1169#[serde(default)]
1170pub struct Representation {
1171 #[serde(rename = "@id")]
1173 pub id: Option<String>,
1174 #[serde(rename = "@bandwidth")]
1176 pub bandwidth: Option<u64>,
1177 #[serde(rename = "@qualityRanking")]
1181 pub qualityRanking: Option<u8>,
1182 #[serde(rename = "@dependencyId")]
1186 pub dependencyId: Option<String>,
1187 #[serde(rename = "@associationId")]
1188 pub associationId: Option<String>,
1189 #[serde(rename = "@associationType")]
1190 pub associationType: Option<String>,
1191 #[serde(rename = "@mediaStreamStructureId")]
1192 pub mediaStreamStructureId: Option<String>,
1193 #[serde(rename = "@profiles")]
1194 pub profiles: Option<String>,
1195 #[serde(rename = "@width")]
1196 pub width: Option<u64>,
1197 #[serde(rename = "@height")]
1198 pub height: Option<u64>,
1199 #[serde(rename = "@sar")]
1201 pub sar: Option<String>,
1202 #[serde(rename = "@frameRate")]
1203 pub frameRate: Option<String>, #[serde(rename = "@audioSamplingRate")]
1205 pub audioSamplingRate: Option<String>,
1206 #[serde(rename = "@mimeType")]
1209 pub mimeType: Option<String>,
1210 #[serde(rename = "@segmentProfiles")]
1213 pub segmentProfiles: Option<String>,
1214 #[serde(rename = "@codecs")]
1217 pub codecs: Option<String>,
1218 #[serde(rename = "@containerProfiles")]
1219 pub containerProfiles: Option<String>,
1220 #[serde(rename = "@maximumSAPPeriod")]
1221 pub maximumSAPPeriod: Option<f64>,
1222 #[serde(rename = "@startWithSAP")]
1223 pub startWithSAP: Option<u64>,
1224 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1226 pub maxPlayoutRate: Option<f64>,
1227 #[serde(rename = "@codingDependency")]
1228 pub codingDependency: Option<bool>,
1229 #[serde(rename = "@scanType")]
1231 pub scanType: Option<String>,
1232 #[serde(rename = "@selectionPriority")]
1233 pub selectionPriority: Option<u64>,
1234 #[serde(rename = "@tag")]
1235 pub tag: Option<String>,
1236 #[serde(rename = "@contentType")]
1237 pub contentType: Option<String>,
1238 #[serde(rename = "@lang")]
1240 pub lang: Option<String>,
1241 #[serde(rename = "@sampleRate")]
1242 pub sampleRate: Option<u64>,
1243 #[serde(rename = "@numChannels")]
1244 pub numChannels: Option<u32>,
1245 #[serde(rename = "@xlink:href", alias = "@href")]
1246 pub href: Option<String>,
1247 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1248 pub actuate: Option<String>,
1249 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1250 pub scte214_supplemental_profiles: Option<String>,
1251 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1252 pub scte214_supplemental_codecs: Option<String>,
1253 pub FramePacking: Vec<FramePacking>,
1254 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1255 pub ContentProtection: Vec<ContentProtection>,
1256 pub OutputProtection: Option<OutputProtection>,
1257 #[serde(rename = "EssentialProperty")]
1258 pub essential_property: Vec<EssentialProperty>,
1259 #[serde(rename = "SupplementalProperty")]
1260 pub supplemental_property: Vec<SupplementalProperty>,
1261 pub InbandEventStream: Vec<InbandEventStream>,
1262 pub Switching: Vec<Switching>,
1263 #[serde(rename = "GroupLabel")]
1265 pub group_label: Vec<Label>,
1266 pub Label: Vec<Label>,
1267 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1268 pub Resync: Vec<Resync>,
1270 pub BaseURL: Vec<BaseURL>,
1271 pub SubRepresentation: Vec<SubRepresentation>,
1273 pub SegmentBase: Option<SegmentBase>,
1274 pub SegmentList: Option<SegmentList>,
1275 pub SegmentTemplate: Option<SegmentTemplate>,
1276 #[serde(rename = "RepresentationIndex")]
1277 pub representation_index: Option<RepresentationIndex>,
1278}
1279
1280#[skip_serializing_none]
1282#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1283#[serde(default)]
1284pub struct ContentComponent {
1285 #[serde(rename = "@id")]
1286 pub id: Option<String>,
1287 #[serde(rename = "@lang")]
1289 pub lang: Option<String>,
1290 #[serde(rename = "@contentType")]
1291 pub contentType: Option<String>,
1292 #[serde(rename = "@par")]
1293 pub par: Option<String>,
1294 #[serde(rename = "@tag")]
1295 pub tag: Option<String>,
1296 pub Accessibility: Vec<Accessibility>,
1297 pub Role: Vec<Role>,
1298 pub Rating: Vec<Rating>,
1299 pub Viewpoint: Vec<Viewpoint>,
1300}
1301
1302#[skip_serializing_none]
1304#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1305#[serde(default)]
1306pub struct CencPssh {
1307 #[serde(rename = "$text")]
1308 pub content: Option<String>,
1309}
1310
1311#[skip_serializing_none]
1313#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1314#[serde(default)]
1315pub struct Laurl {
1316 #[serde(rename = "@Lic_type")]
1317 pub lic_type: Option<String>,
1318 #[serde(rename = "$text")]
1319 pub content: Option<String>,
1320}
1321
1322#[skip_serializing_none]
1324#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1325#[serde(default)]
1326pub struct MsprPro {
1327 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
1328 pub xmlns: Option<String>,
1329 #[serde(rename = "$text")]
1330 pub content: Option<String>,
1331}
1332
1333#[skip_serializing_none]
1334#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1335#[serde(default)]
1336pub struct MsprIsEncrypted {
1337 #[serde(rename = "$text")]
1338 pub content: Option<String>,
1339}
1340
1341#[skip_serializing_none]
1342#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1343#[serde(default)]
1344pub struct MsprIVSize {
1345 #[serde(rename = "$text")]
1346 pub content: Option<String>,
1347}
1348
1349#[skip_serializing_none]
1350#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1351#[serde(default)]
1352pub struct MsprKid {
1353 #[serde(rename = "$text")]
1354 pub content: Option<String>,
1355}
1356
1357#[skip_serializing_none]
1358#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1359#[serde(default)]
1360pub struct OutputProtection {
1361 #[serde(rename = "@schemeIdUri")]
1362 pub schemeIdUri: String,
1363 #[serde(rename = "@value")]
1364 pub value: Option<String>,
1365 #[serde(rename = "@id")]
1366 pub id: Option<String>,
1367}
1368
1369#[skip_serializing_none]
1374#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1375#[serde(default)]
1376pub struct ContentProtection {
1377 #[serde(rename = "@robustness")]
1379 pub robustness: Option<String>,
1380 #[serde(rename = "@refId")]
1381 pub refId: Option<String>,
1382 #[serde(rename = "@ref")]
1384 pub r#ref: Option<String>,
1385 #[serde(rename = "@schemeIdUri")]
1387 pub schemeIdUri: String,
1388 #[serde(rename = "@value")]
1389 pub value: Option<String>,
1390 #[serde(rename = "@id")]
1391 pub id: Option<String>,
1392 #[serde(rename="cenc:pssh", alias="pssh")]
1394 pub cenc_pssh: Vec<CencPssh>,
1395 #[serde(rename = "@cenc:default_KID", alias = "@default_KID")]
1397 pub default_KID: Option<String>,
1398 #[serde(rename = "dashif:laurl", alias = "laurl")]
1400 pub laurl: Option<Laurl>,
1401 #[serde(rename = "clearkey:Laurl", alias = "Laurl")]
1405 pub clearkey_laurl: Option<Laurl>,
1406 #[serde(rename = "mspr:pro", alias = "pro")]
1408 pub msprpro: Option<MsprPro>,
1409 #[serde(rename = "mspr:IsEncrypted", alias = "IsEncrypted")]
1410 pub mspr_is_encrypted: Option<MsprIsEncrypted>,
1411 #[serde(rename = "mspr:IV_Size", alias = "IV_Size")]
1412 pub mspr_iv_size: Option<MsprIVSize>,
1413 #[serde(rename = "mspr:kid", alias = "kid")]
1414 pub mspr_kid: Option<MsprKid>,
1415}
1416
1417#[skip_serializing_none]
1423#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1424#[serde(default)]
1425pub struct Role {
1426 #[serde(rename = "@id")]
1427 pub id: Option<String>,
1428 #[serde(rename = "@schemeIdUri")]
1429 pub schemeIdUri: String,
1430 #[serde(rename = "@value")]
1431 pub value: Option<String>,
1432}
1433
1434#[skip_serializing_none]
1435#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1436#[serde(default)]
1437pub struct Viewpoint {
1438 #[serde(rename = "@id")]
1439 pub id: Option<String>,
1440 #[serde(rename = "@schemeIdUri")]
1441 pub schemeIdUri: String,
1442 #[serde(rename = "@value")]
1443 pub value: Option<String>,
1444}
1445
1446#[skip_serializing_none]
1447#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1448#[serde(default)]
1449pub struct Selection {
1450 #[serde(rename = "@dataEncoding")]
1451 pub dataEncoding: Option<String>,
1452 #[serde(rename = "@parameter")]
1453 pub parameter: Option<String>,
1454 #[serde(rename = "@data")]
1455 pub data: Option<String>,
1456}
1457
1458#[skip_serializing_none]
1459#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1460#[serde(default)]
1461pub struct SelectionInfo {
1462 #[serde(rename = "@selectionInfo")]
1463 pub selectionInfo: Option<String>,
1464 #[serde(rename = "@contactURL")]
1465 pub contactURL: Option<String>,
1466 pub Selection: Vec<Selection>,
1467}
1468
1469#[skip_serializing_none]
1476#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1477#[serde(default)]
1478pub struct Event {
1479 #[serde(rename = "@id")]
1480 pub id: Option<String>,
1481 #[serde(rename = "@presentationTime", default = "default_optu64_zero")]
1482 pub presentationTime: Option<u64>,
1483 #[serde(rename = "@presentationTimeOffset")]
1484 pub presentationTimeOffset: Option<u64>,
1485 #[serde(rename = "@duration")]
1486 pub duration: Option<u64>,
1487 #[serde(rename = "@timescale")]
1488 pub timescale: Option<u64>,
1489 #[serde(rename = "@contentEncoding")]
1492 pub contentEncoding: Option<String>,
1493 #[serde(rename = "@messageData")]
1496 pub messageData: Option<String>,
1497 pub SelectionInfo: Option<SelectionInfo>,
1498 #[cfg(feature = "scte35")]
1499 #[serde(rename = "scte35:Signal", alias="Signal")]
1500 #[cfg(feature = "scte35")]
1501 pub signal: Vec<Signal>,
1502 #[cfg(feature = "scte35")]
1503 #[serde(rename = "scte35:SpliceInfoSection", alias="SpliceInfoSection")]
1504 #[cfg(feature = "scte35")]
1505 pub splice_info_section: Vec<SpliceInfoSection>,
1506 #[serde(rename = "@value")]
1509 pub value: Option<String>,
1510 #[serde(rename = "$text")]
1513 pub content: Option<String>,
1514}
1515
1516#[skip_serializing_none]
1517#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1518#[serde(default)]
1519pub struct EventStream {
1520 #[serde(rename = "@xlink:href")]
1521 #[serde(alias = "@href")]
1522 pub href: Option<String>,
1523 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1524 pub actuate: Option<String>,
1525 #[serde(rename = "@messageData")]
1526 pub messageData: Option<String>,
1528 #[serde(rename = "@schemeIdUri")]
1529 pub schemeIdUri: String,
1530 #[serde(rename = "@value")]
1531 pub value: Option<String>,
1532 #[serde(rename = "@timescale")]
1533 pub timescale: Option<u64>,
1534 #[serde(rename = "@presentationTimeOffset")]
1535 pub presentationTimeOffset: Option<u64>,
1536 #[serde(rename = "Event")]
1537 pub event: Vec<Event>,
1538}
1539
1540#[skip_serializing_none]
1546#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1547#[serde(default)]
1548pub struct InbandEventStream {
1549 #[serde(rename = "@timescale")]
1550 pub timescale: Option<u64>,
1551 #[serde(rename = "@schemeIdUri")]
1552 pub schemeIdUri: String,
1553 #[serde(rename = "Event")]
1554 pub event: Vec<Event>,
1555 #[serde(rename = "@value")]
1556 pub value: Option<String>,
1557 #[serde(rename = "@xlink:href")]
1559 #[serde(alias = "@href")]
1560 pub href: Option<String>,
1561 #[serde(rename = "@xlink:actuate", alias = "@actuate")]
1562 pub actuate: Option<String>,
1563}
1564
1565#[skip_serializing_none]
1566#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1567#[serde(default)]
1568pub struct EssentialProperty {
1569 #[serde(rename = "@id")]
1570 pub id: Option<String>,
1571 #[serde(rename = "@schemeIdUri")]
1572 pub schemeIdUri: String,
1573 #[serde(rename = "@value")]
1574 pub value: Option<String>,
1575}
1576
1577#[skip_serializing_none]
1578#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1579#[serde(default)]
1580pub struct SupplementalProperty {
1581 #[serde(rename = "@id")]
1582 pub id: Option<String>,
1583 #[serde(rename = "@schemeIdUri")]
1584 pub schemeIdUri: String,
1585 #[serde(rename = "@value")]
1586 pub value: Option<String>,
1587 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1588 #[serde(rename(deserialize = "ContentIdentifier"))]
1589 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1590}
1591
1592#[skip_serializing_none]
1595#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1596#[serde(default)]
1597pub struct Label {
1598 #[serde(rename = "@id")]
1599 pub id: Option<String>,
1600 #[serde(rename = "@lang")]
1601 pub lang: Option<String>,
1602 #[serde(rename = "$text")]
1603 pub content: String,
1604}
1605
1606#[skip_serializing_none]
1614#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1615#[serde(default)]
1616pub struct AdaptationSet {
1617 #[serde(rename = "@id")]
1618 pub id: Option<String>,
1619 #[serde(rename = "@xlink:href", alias = "@href")]
1621 pub href: Option<String>,
1622 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1623 pub actuate: Option<String>,
1624 #[serde(rename = "@group")]
1625 pub group: Option<i64>,
1626 #[serde(rename = "@selectionPriority")]
1627 pub selectionPriority: Option<u64>,
1628 #[serde(rename = "@contentType")]
1630 pub contentType: Option<String>,
1631 #[serde(rename = "@profiles")]
1632 pub profiles: Option<String>,
1633 #[serde(rename = "@lang")]
1635 pub lang: Option<String>,
1636 #[serde(rename = "@sar")]
1638 pub sar: Option<String>,
1639 #[serde(rename = "@par")]
1641 pub par: Option<String>,
1642 #[serde(rename = "@scanType")]
1644 pub scanType: Option<String>,
1645 #[serde(rename = "@segmentAlignment")]
1646 pub segmentAlignment: Option<bool>,
1647 #[serde(rename = "@segmentProfiles")]
1648 pub segmentProfiles: Option<String>,
1651 #[serde(rename = "@subsegmentAlignment")]
1652 pub subsegmentAlignment: Option<bool>,
1653 #[serde(rename = "@subsegmentStartsWithSAP")]
1654 pub subsegmentStartsWithSAP: Option<u64>,
1655 #[serde(rename = "@bitstreamSwitching")]
1656 pub bitstreamSwitching: Option<bool>,
1657 #[serde(rename = "@audioSamplingRate")]
1658 pub audioSamplingRate: Option<String>,
1659 #[serde(rename = "@width")]
1660 pub width: Option<u64>,
1661 #[serde(rename = "@height")]
1662 pub height: Option<u64>,
1663 #[serde(rename = "@mimeType")]
1665 pub mimeType: Option<String>,
1666 #[serde(rename = "@codecs")]
1668 pub codecs: Option<String>,
1669 #[serde(rename = "@minBandwidth")]
1670 pub minBandwidth: Option<u64>,
1671 #[serde(rename = "@maxBandwidth")]
1672 pub maxBandwidth: Option<u64>,
1673 #[serde(rename = "@minWidth")]
1674 pub minWidth: Option<u64>,
1675 #[serde(rename = "@maxWidth")]
1676 pub maxWidth: Option<u64>,
1677 #[serde(rename = "@minHeight")]
1678 pub minHeight: Option<u64>,
1679 #[serde(rename = "@maxHeight")]
1680 pub maxHeight: Option<u64>,
1681 #[serde(rename = "@frameRate")]
1682 pub frameRate: Option<String>, #[serde(rename = "@minFrameRate")]
1684 pub minFrameRate: Option<String>, #[serde(rename = "@maxFrameRate")]
1686 pub maxFrameRate: Option<String>, #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1689 pub maxPlayoutRate: Option<f64>,
1690 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1691 pub maximumSAPPeriod: Option<f64>,
1692 #[serde(rename = "@startWithSAP")]
1693 pub startWithSAP: Option<u64>,
1694 #[serde(rename = "@codingDependency")]
1695 pub codingDependency: Option<bool>,
1696 pub FramePacking: Vec<FramePacking>,
1697 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1698 pub ContentProtection: Vec<ContentProtection>,
1699 #[serde(rename = "EssentialProperty")]
1701 pub essential_property: Vec<EssentialProperty>,
1702 #[serde(rename = "SupplementalProperty")]
1703 pub supplemental_property: Vec<SupplementalProperty>,
1704 pub InbandEventStream: Vec<InbandEventStream>,
1705 pub Switching: Vec<Switching>,
1706 pub GroupLabel: Vec<Label>,
1708 pub Label: Vec<Label>,
1709 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1710 pub Resync: Vec<Resync>,
1712 pub Accessibility: Vec<Accessibility>,
1713 pub Role: Vec<Role>,
1714 pub Rating: Vec<Rating>,
1715 pub Viewpoint: Vec<Viewpoint>,
1716 pub ContentComponent: Vec<ContentComponent>,
1717 pub BaseURL: Vec<BaseURL>,
1718 pub SegmentBase: Option<SegmentBase>,
1719 pub SegmentList: Option<SegmentList>,
1720 pub SegmentTemplate: Option<SegmentTemplate>,
1721 #[serde(rename = "Representation")]
1722 pub representations: Vec<Representation>,
1723 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1724 pub scte214_supplemental_profiles: Option<String>,
1725 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1726 pub scte214_supplemental_codecs: Option<String>,
1727}
1728
1729#[skip_serializing_none]
1734#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1735#[serde(default)]
1736pub struct AssetIdentifier {
1737 #[serde(rename = "@schemeIdUri")]
1738 pub schemeIdUri: String,
1739 #[serde(rename = "@value")]
1740 pub value: Option<String>,
1741 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1742 #[serde(rename(deserialize = "ContentIdentifier"))]
1743 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1744}
1745
1746#[skip_serializing_none]
1751#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1752#[serde(default)]
1753pub struct Subset {
1754 #[serde(rename = "@id")]
1755 pub id: Option<String>,
1756 #[serde(rename = "@contains",
1759 deserialize_with = "deserialize_xsd_uintvector",
1760 serialize_with = "serialize_xsd_uintvector",
1761 default)]
1762 pub contains: Vec<u64>,
1763}
1764
1765#[skip_serializing_none]
1768#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1769#[serde(default)]
1770pub struct Period {
1771 #[serde(rename = "@xlink:href", alias = "@href")]
1773 pub href: Option<String>,
1774
1775 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
1776 pub actuate: Option<String>,
1777
1778 #[serde(rename = "@id")]
1779 pub id: Option<String>,
1780
1781 #[serde(rename = "@start",
1783 serialize_with = "serialize_xs_duration",
1784 deserialize_with = "deserialize_xs_duration",
1785 default)]
1786 pub start: Option<Duration>,
1787
1788 #[serde(rename = "@duration",
1790 serialize_with = "serialize_xs_duration",
1791 deserialize_with = "deserialize_xs_duration",
1792 default)]
1793 pub duration: Option<Duration>,
1794
1795 #[serde(rename = "@bitstreamSwitching", default)]
1797 pub bitstreamSwitching: Option<bool>,
1798
1799 pub BaseURL: Vec<BaseURL>,
1800
1801 pub SegmentBase: Option<SegmentBase>,
1802
1803 pub SegmentList: Option<SegmentList>,
1804
1805 pub SegmentTemplate: Option<SegmentTemplate>,
1806
1807 #[serde(rename = "AssetIdentifier")]
1808 pub asset_identifier: Option<AssetIdentifier>,
1809
1810 #[serde(rename = "EventStream")]
1811 pub event_streams: Vec<EventStream>,
1812
1813 #[serde(rename = "ServiceDescription")]
1814 pub service_description: Vec<ServiceDescription>,
1815
1816 pub ContentProtection: Vec<ContentProtection>,
1817
1818 #[serde(rename = "AdaptationSet")]
1819 pub adaptations: Vec<AdaptationSet>,
1820
1821 #[serde(rename = "Subset")]
1822 pub subsets: Vec<Subset>,
1823
1824 #[serde(rename = "SupplementalProperty")]
1825 pub supplemental_property: Vec<SupplementalProperty>,
1826
1827 #[serde(rename = "EmptyAdaptationSet")]
1828 pub empty_adaptations: Vec<AdaptationSet>,
1829
1830 #[serde(rename = "GroupLabel")]
1831 pub group_label: Vec<Label>,
1832
1833 #[serde(rename = "Preselection")]
1834 pub pre_selections: Vec<Preselection>,
1835
1836 #[serde(rename = "EssentialProperty")]
1837 pub essential_property: Vec<EssentialProperty>,
1838}
1839
1840#[skip_serializing_none]
1841#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1842#[serde(default)]
1843pub struct Reporting {
1844 #[serde(rename = "@id")]
1845 pub id: Option<String>,
1846 #[serde(rename = "@schemeIdUri")]
1847 pub schemeIdUri: String,
1848 #[serde(rename = "@value")]
1849 pub value: Option<String>,
1850 #[serde(rename = "@dvb:reportingUrl", alias = "@reportingUrl")]
1851 pub reportingUrl: Option<String>,
1852 #[serde(rename = "@dvb:probability", alias = "@probability")]
1853 pub probability: Option<u64>,
1854}
1855
1856#[skip_serializing_none]
1857#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1858#[serde(default)]
1859pub struct Range {
1860 #[serde(rename = "@starttime",
1861 serialize_with = "serialize_xs_duration",
1862 deserialize_with = "deserialize_xs_duration",
1863 default)]
1864 pub starttime: Option<Duration>,
1865 #[serde(rename = "@duration",
1866 serialize_with = "serialize_xs_duration",
1867 deserialize_with = "deserialize_xs_duration",
1868 default)]
1869 pub duration: Option<Duration>,
1870}
1871
1872#[skip_serializing_none]
1873#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1874#[serde(default)]
1875pub struct Metrics {
1876 #[serde(rename = "@metrics")]
1877 pub metrics: String,
1878 pub Reporting: Vec<Reporting>,
1879 pub Range: Vec<Range>,
1880}
1881
1882#[skip_serializing_none]
1884#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1885#[serde(default)]
1886pub struct Latency {
1887 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1888 pub min: Option<f64>,
1889 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1890 pub max: Option<f64>,
1891 #[serde(rename = "@target", serialize_with="serialize_opt_xsd_double")]
1892 pub target: Option<f64>,
1893 #[serde(rename = "@referenceId")]
1894 pub referenceId: Option<String>,
1895}
1896
1897#[skip_serializing_none]
1899#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1900#[serde(default)]
1901pub struct PlaybackRate {
1902 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1903 pub min: Option<f64>,
1904 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1905 pub max: Option<f64>,
1906}
1907
1908#[skip_serializing_none]
1910#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1911#[serde(default)]
1912pub struct OperatingQuality {
1913 #[serde(default = "default_optstring_any")]
1914 pub mediaType: Option<String>,
1915 #[serde(rename = "@min")]
1916 pub min: Option<u64>,
1917 #[serde(rename = "@max")]
1918 pub max: Option<u64>,
1919 #[serde(rename = "@target")]
1920 pub target: Option<u64>,
1921 #[serde(rename = "@type")]
1922 pub _type: Option<String>,
1923 #[serde(rename = "@maxDifference")]
1924 pub maxDifference: Option<u64>,
1925}
1926
1927#[skip_serializing_none]
1929#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1930#[serde(default)]
1931pub struct OperatingBandwidth {
1932 #[serde(rename = "@mediaType", default = "default_optstring_any")]
1933 pub mediaType: Option<String>,
1934 #[serde(rename = "@min")]
1935 pub min: Option<u64>,
1936 #[serde(rename = "@max")]
1937 pub max: Option<u64>,
1938 #[serde(rename = "@target")]
1939 pub target: Option<u64>,
1940}
1941
1942#[skip_serializing_none]
1943#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1944#[serde(default)]
1945pub struct ContentSteering {
1946 #[serde(rename = "@defaultServiceLocation")]
1947 pub defaultServiceLocation: Option<String>,
1948 #[serde(rename = "@queryBeforeStart", default = "default_optbool_false")]
1949 pub queryBeforeStart: Option<bool>,
1950 #[serde(rename = "@clientRequirement", default = "default_optbool_true")]
1951 pub clientRequirement: Option<bool>,
1952}
1953
1954#[skip_serializing_none]
1955#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1956#[serde(default)]
1957pub struct CMCDParameters {
1958 #[serde(rename = "@version", default = "default_optu64_one")]
1959 pub version: Option<u64>,
1960 #[serde(rename = "@mode", default = "default_optstring_query")]
1961 pub mode: Option<String>,
1962 #[serde(rename = "@includeInRequests", default = "default_optstring_segment")]
1963 pub includeInRequests: Option<String>,
1964 #[serde(rename = "@keys")]
1965 pub keys: String,
1966 #[serde(rename = "@contentID")]
1967 pub contentID: Option<String>,
1968 #[serde(rename = "@sessionID")]
1969 pub sessionID: Option<String>,
1970}
1971
1972#[skip_serializing_none]
1974#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1975#[serde(default)]
1976pub struct ClientDataReporting {
1977 pub CMCDParameters: Vec<CMCDParameters>,
1978 #[serde(rename = "@serviceLocations")]
1979 pub serviceLocations: Option<String>,
1980 #[serde(rename = "@adaptationSets")]
1981 pub adaptationSets: Option<String>,
1982}
1983
1984#[skip_serializing_none]
1985#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1986#[serde(default)]
1987pub struct PlaybackRestrictions {
1988 #[serde(rename = "@skipAfter",
1989 serialize_with = "serialize_xs_duration",
1990 deserialize_with = "deserialize_xs_duration",
1991 default)]
1992 pub skipAfter: Option<Duration>,
1993}
1994
1995#[skip_serializing_none]
1996#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1997#[serde(default)]
1998pub struct ServiceDescription {
1999 #[serde(rename = "Scope")]
2000 pub scopes: Vec<Scope>,
2001 pub Latency: Vec<Latency>,
2002 pub PlaybackRate: Vec<PlaybackRate>,
2003 pub OperatingQuality: Vec<OperatingQuality>,
2004 pub OperatingBandwidth: Vec<OperatingBandwidth>,
2005 pub ContentSteering: Vec<ContentSteering>,
2006 pub ClientDataReporting: Vec<ClientDataReporting>,
2007 pub PlaybackRestrictions: Vec<PlaybackRestrictions>,
2008 #[serde(rename = "@id")]
2009 pub id: Option<String>,
2010}
2011
2012#[skip_serializing_none]
2014#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2015#[serde(default)]
2016pub struct UTCTiming {
2017 #[serde(rename = "@id")]
2018 pub id: Option<String>,
2019 #[serde(rename = "@schemeIdUri")]
2022 pub schemeIdUri: String,
2023 #[serde(rename = "@value")]
2024 pub value: Option<String>,
2025}
2026
2027#[skip_serializing_none]
2032#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2033#[serde(default)]
2034pub struct ProducerReferenceTime {
2035 #[serde(rename = "@id")]
2037 pub id: Option<String>,
2038 #[serde(rename = "@inband", default = "default_optbool_false")]
2039 pub inband: Option<bool>,
2040 #[serde(rename = "@presentationTime")]
2042 pub presentationTime: Option<u64>,
2043 #[serde(rename = "@type", default = "default_optstring_encoder")]
2044 pub prtType: Option<String>,
2045 #[serde(rename = "@wallClockTime",
2049 alias="@wallclockTime",
2050 deserialize_with = "deserialize_xs_datetime",
2051 default)]
2052 pub wallClockTime: Option<XsDatetime>,
2053 pub UTCTiming: Option<UTCTiming>,
2054}
2055
2056#[skip_serializing_none]
2057#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2058#[serde(default)]
2059pub struct LeapSecondInformation {
2060 #[serde(rename = "@availabilityStartLeapOffset")]
2061 pub availabilityStartLeapOffset: Option<i64>,
2062 #[serde(rename = "@nextAvailabilityStartLeapOffset")]
2063 pub nextAvailabilityStartLeapOffset: Option<i64>,
2064 #[serde(rename = "@nextLeapChangeTime",
2065 deserialize_with = "deserialize_xs_datetime",
2066 default)]
2067 pub nextLeapChangeTime: Option<XsDatetime>,
2068}
2069
2070#[skip_serializing_none]
2077#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2078#[serde(default)]
2079pub struct PatchLocation {
2080 #[serde(rename = "@ttl", serialize_with="serialize_opt_xsd_double")]
2081 pub ttl: Option<f64>,
2082 #[serde(rename = "$text")]
2083 pub content: String,
2084}
2085
2086#[skip_serializing_none]
2088#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2089#[serde(default)]
2090pub struct MPD {
2091 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
2092 pub xmlns: Option<String>,
2093 #[serde(rename = "@id")]
2094 pub id: Option<String>,
2095 #[serde(rename = "@profiles")]
2096 pub profiles: Option<String>,
2097 #[serde(rename = "@type")]
2100 pub mpdtype: Option<String>,
2101 #[serde(rename = "@availabilityStartTime",
2102 deserialize_with = "deserialize_xs_datetime",
2103 default)]
2104 pub availabilityStartTime: Option<XsDatetime>,
2105 #[serde(rename = "@availabilityEndTime",
2106 deserialize_with = "deserialize_xs_datetime",
2107 default)]
2108 pub availabilityEndTime: Option<XsDatetime>,
2109 #[serde(rename = "@publishTime",
2110 deserialize_with = "deserialize_xs_datetime",
2111 default)]
2112 pub publishTime: Option<XsDatetime>,
2113 #[serde(rename = "@mediaPresentationDuration",
2114 serialize_with = "serialize_xs_duration",
2115 deserialize_with = "deserialize_xs_duration",
2116 default)]
2117 pub mediaPresentationDuration: Option<Duration>,
2118 #[serde(rename = "@minimumUpdatePeriod",
2119 serialize_with = "serialize_xs_duration",
2120 deserialize_with = "deserialize_xs_duration",
2121 default)]
2122 pub minimumUpdatePeriod: Option<Duration>,
2123 #[serde(rename = "@minBufferTime",
2125 serialize_with = "serialize_xs_duration",
2126 deserialize_with = "deserialize_xs_duration",
2127 default)]
2128 pub minBufferTime: Option<Duration>,
2129 #[serde(rename = "@timeShiftBufferDepth",
2132 serialize_with = "serialize_xs_duration",
2133 deserialize_with = "deserialize_xs_duration",
2134 default)]
2135 pub timeShiftBufferDepth: Option<Duration>,
2136 #[serde(rename = "@suggestedPresentationDelay",
2138 serialize_with = "serialize_xs_duration",
2139 deserialize_with = "deserialize_xs_duration",
2140 default)]
2141 pub suggestedPresentationDelay: Option<Duration>,
2142 #[serde(rename = "@maxSegmentDuration",
2143 serialize_with = "serialize_xs_duration",
2144 deserialize_with = "deserialize_xs_duration",
2145 default)]
2146 pub maxSegmentDuration: Option<Duration>,
2147 #[serde(rename = "@maxSubsegmentDuration",
2148 serialize_with = "serialize_xs_duration",
2149 deserialize_with = "deserialize_xs_duration",
2150 default)]
2151 pub maxSubsegmentDuration: Option<Duration>,
2152 #[serialize_always]
2154 #[serde(rename="@xmlns:xsi", alias="@xsi", serialize_with="serialize_xsi_ns")]
2155 pub xsi: Option<String>,
2156 #[serde(alias = "@ext", rename = "@xmlns:ext")]
2157 pub ext: Option<String>,
2158 #[serialize_always]
2160 #[serde(rename="@xmlns:cenc", alias="@cenc", serialize_with="serialize_cenc_ns")]
2161 pub cenc: Option<String>,
2162 #[serialize_always]
2164 #[serde(rename="@xmlns:mspr", alias="@mspr", serialize_with="serialize_mspr_ns")]
2165 pub mspr: Option<String>,
2166 #[serialize_always]
2168 #[serde(rename="@xmlns:xlink", alias="@xlink", serialize_with="serialize_xlink_ns")]
2169 pub xlink: Option<String>,
2170 #[cfg(feature = "scte35")]
2173 #[serialize_always]
2174 #[serde(rename="@xmlns:scte35", alias="@scte35", serialize_with="scte35::serialize_scte35_ns")]
2175 pub scte35: Option<String>,
2176 #[serialize_always]
2179 #[serde(rename="@xmlns:dvb", alias="@dvb", serialize_with="serialize_dvb_ns")]
2180 pub dvb: Option<String>,
2181 #[serde(rename = "@xsi:schemaLocation", alias = "@schemaLocation")]
2182 pub schemaLocation: Option<String>,
2183 #[serde(alias = "@scte214", rename = "@xmlns:scte214")]
2185 pub scte214: Option<String>,
2186 pub ProgramInformation: Vec<ProgramInformation>,
2187 #[serde(rename = "BaseURL")]
2189 pub base_url: Vec<BaseURL>,
2190 #[serde(rename = "Location", default)]
2191 pub locations: Vec<Location>,
2192 pub PatchLocation: Vec<PatchLocation>,
2195 pub ServiceDescription: Vec<ServiceDescription>,
2196 pub ContentProtection: Vec<ContentProtection>,
2198 #[serde(rename = "Period", default)]
2199 pub periods: Vec<Period>,
2200 pub Metrics: Vec<Metrics>,
2201 #[serde(rename = "EssentialProperty")]
2202 pub essential_property: Vec<EssentialProperty>,
2203 #[serde(rename = "SupplementalProperty")]
2204 pub supplemental_property: Vec<SupplementalProperty>,
2205 pub UTCTiming: Vec<UTCTiming>,
2206 pub LeapSecondInformation: Option<LeapSecondInformation>,
2208}
2209
2210impl std::fmt::Display for MPD {
2211 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2212 write!(f, "{}", quick_xml::se::to_string(self).map_err(|_| std::fmt::Error)?)
2213 }
2214}
2215
2216pub fn parse(xml: &str) -> Result<MPD, DashMpdError> {
2218 #[cfg(feature = "warn_ignored_elements")]
2219 {
2220 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2221 let _: MPD = serde_ignored::deserialize(xd, |path| {
2222 warn!("Unused XML element in manifest: {path}");
2223 }).map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2224 }
2225 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2226 let mpd: MPD = serde_path_to_error::deserialize(xd)
2227 .map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2228 Ok(mpd)
2229}
2230
2231
2232fn is_audio_codec(name: &str) -> bool {
2234 name.starts_with("mp4a") ||
2235 name.starts_with("aac") ||
2236 name.starts_with("vorbis") ||
2237 name.starts_with("opus") ||
2238 name.starts_with("ogg") ||
2239 name.starts_with("webm") ||
2240 name.starts_with("flac") ||
2241 name.starts_with("mp3") ||
2242 name.starts_with("mpeg") ||
2243 name.starts_with("3gpp") ||
2244 name.starts_with("wav") ||
2245 name.starts_with("ec-3") ||
2246 name.starts_with("ac-4") ||
2247 name.starts_with("dtsc") ||
2248 name.starts_with("aptx") ||
2249 name.starts_with("aiff") ||
2250 name.starts_with("mha1") }
2252
2253
2254pub fn is_audio_adaptation(a: &&AdaptationSet) -> bool {
2260 if let Some(codec) = &a.codecs {
2261 if is_audio_codec(codec) {
2262 return true;
2263 }
2264 }
2265 if let Some(ct) = &a.contentType {
2266 if ct == "audio" {
2267 return true;
2268 }
2269 }
2270 if let Some(mimetype) = &a.mimeType {
2271 if mimetype.starts_with("audio/") {
2272 return true;
2273 }
2274 }
2275 for r in &a.representations {
2276 if let Some(ct) = &r.contentType {
2277 if ct == "audio" {
2278 return true;
2279 }
2280 }
2281 if let Some(mimetype) = &r.mimeType {
2282 if mimetype.starts_with("audio/") {
2283 return true;
2284 }
2285 }
2286 }
2287 false
2288}
2289
2290pub fn is_video_adaptation(a: &&AdaptationSet) -> bool {
2299 if is_audio_adaptation(a) {
2300 return false;
2301 }
2302 if let Some(ct) = &a.contentType {
2303 if ct == "video" {
2304 return true;
2305 }
2306 }
2307 if let Some(mimetype) = &a.mimeType {
2308 if mimetype.starts_with("video/") {
2309 return true;
2310 }
2311 }
2312 for r in &a.representations {
2313 if let Some(ct) = &r.contentType {
2314 if ct == "video" {
2315 return true;
2316 }
2317 }
2318 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2321 return false;
2322 }
2323 if let Some(mimetype) = &r.mimeType {
2324 if mimetype.starts_with("video/") {
2325 return true;
2326 }
2327 }
2328 }
2329 false
2330}
2331
2332
2333fn is_subtitle_mimetype(mt: &str) -> bool {
2334 mt.eq("text/vtt") ||
2335 mt.eq("application/ttml+xml") ||
2336 mt.eq("application/x-sami")
2337
2338 }
2341
2342fn is_subtitle_codec(c: &str) -> bool {
2343 c == "wvtt" ||
2344 c == "c608" ||
2345 c == "stpp" ||
2346 c == "tx3g" ||
2347 c.starts_with("stpp.")
2348}
2349
2350pub fn is_subtitle_adaptation(a: &&AdaptationSet) -> bool {
2362 if a.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2363 return true;
2364 }
2365 if a.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2366 return true;
2367 }
2368 if a.codecs.as_deref().is_some_and(is_subtitle_codec) {
2369 return true;
2370 }
2371 for cc in a.ContentComponent.iter() {
2372 if cc.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2373 return true;
2374 }
2375 }
2376 for r in a.representations.iter() {
2377 if r.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2378 return true;
2379 }
2380 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2382 return true;
2383 }
2384 }
2385 false
2386}
2387
2388
2389#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2391pub enum SubtitleType {
2392 Vtt,
2394 Srt,
2396 Sub,
2398 Ass,
2400 Ttxt,
2402 Ttml,
2404 Sami,
2406 Wvtt,
2410 Stpp,
2412 Eia608,
2414 Unknown,
2415}
2416
2417fn subtitle_type_for_mimetype(mt: &str) -> Option<SubtitleType> {
2418 match mt {
2419 "text/vtt" => Some(SubtitleType::Vtt),
2420 "application/ttml+xml" => Some(SubtitleType::Ttml),
2421 "application/x-sami" => Some(SubtitleType::Sami),
2422 _ => None
2423 }
2424}
2425
2426#[must_use]
2427pub fn subtitle_type(a: &&AdaptationSet) -> SubtitleType {
2428 if let Some(mimetype) = &a.mimeType {
2429 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2430 return st;
2431 }
2432 }
2433 if let Some(codecs) = &a.codecs {
2434 if codecs == "wvtt" {
2435 return SubtitleType::Wvtt;
2437 }
2438 if codecs == "c608" {
2439 return SubtitleType::Eia608;
2440 }
2441 if codecs == "tx3g" {
2442 return SubtitleType::Ttxt;
2443 }
2444 if codecs == "stpp" {
2445 return SubtitleType::Stpp;
2446 }
2447 if codecs.starts_with("stpp.") {
2448 return SubtitleType::Stpp;
2449 }
2450 }
2451 for r in a.representations.iter() {
2452 if let Some(mimetype) = &r.mimeType {
2453 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2454 return st;
2455 }
2456 }
2457 if let Some(codecs) = &r.codecs {
2458 if codecs == "wvtt" {
2459 return SubtitleType::Wvtt;
2460 }
2461 if codecs == "c608" {
2462 return SubtitleType::Eia608;
2463 }
2464 if codecs == "tx3g" {
2465 return SubtitleType::Ttxt;
2466 }
2467 if codecs == "stpp" {
2468 return SubtitleType::Stpp;
2469 }
2470 if codecs.starts_with("stpp.") {
2471 return SubtitleType::Stpp;
2472 }
2473 }
2474 }
2475 SubtitleType::Unknown
2476}
2477
2478
2479#[allow(dead_code)]
2480fn content_protection_type(cp: &ContentProtection) -> String {
2481 if let Some(v) = &cp.value {
2482 if v.eq("cenc") {
2483 return String::from("cenc");
2484 }
2485 if v.eq("Widevine") {
2486 return String::from("Widevine");
2487 }
2488 if v.eq("MSPR 2.0") {
2489 return String::from("PlayReady");
2490 }
2491 }
2492 let uri = &cp.schemeIdUri;
2494 let uri = uri.to_lowercase();
2495 if uri.eq("urn:mpeg:dash:mp4protection:2011") {
2496 return String::from("cenc");
2497 }
2498 if uri.eq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed") {
2499 return String::from("Widevine");
2500 }
2501 if uri.eq("urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95") {
2502 return String::from("PlayReady");
2503 }
2504 if uri.eq("urn:uuid:94ce86fb-07ff-4f43-adb8-93d2fa968ca2") {
2505 return String::from("FairPlay");
2506 }
2507 if uri.eq("urn:uuid:3ea8778f-7742-4bf9-b18b-e834b2acbd47") {
2508 return String::from("Clear Key AES-128");
2509 }
2510 if uri.eq("urn:uuid:be58615b-19c4-4684-88b3-c8c57e99e957") {
2511 return String::from("Clear Key SAMPLE-AES");
2512 }
2513 if uri.eq("urn:uuid:adb41c24-2dbf-4a6d-958b-4457c0d27b95") {
2514 return String::from("Nagra");
2515 }
2516 if uri.eq("urn:uuid:5e629af5-38da-4063-8977-97ffbd9902d4") {
2517 return String::from("Marlin");
2518 }
2519 if uri.eq("urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb") {
2520 return String::from("Adobe PrimeTime");
2521 }
2522 if uri.eq("urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b") {
2523 return String::from("W3C Common PSSH box");
2524 }
2525 if uri.eq("urn:uuid:80a6be7e-1448-4c37-9e70-d5aebe04c8d2") {
2526 return String::from("Irdeto Content Protection");
2527 }
2528 if uri.eq("urn:uuid:3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c") {
2529 return String::from("WisePlay-ChinaDRM");
2530 }
2531 if uri.eq("urn:uuid:616c7469-6361-7374-2d50-726f74656374") {
2532 return String::from("Alticast");
2533 }
2534 if uri.eq("urn:uuid:6dd8b3c3-45f4-4a68-bf3a-64168d01a4a6") {
2535 return String::from("ABV DRM");
2536 }
2537 if uri.eq("urn:mpeg:dash:sea:2012") {
2539 return String::from("SEA");
2540 }
2541 String::from("<unknown>")
2542}
2543
2544
2545fn check_segment_template_duration(
2546 st: &SegmentTemplate,
2547 max_seg_duration: &Duration,
2548 outer_timescale: u64) -> Vec<String>
2549{
2550 let mut errors = Vec::new();
2551 if let Some(timeline) = &st.SegmentTimeline {
2552 for s in &timeline.segments {
2553 let sd = s.d / st.timescale.unwrap_or(outer_timescale);
2554 if sd > max_seg_duration.as_secs() {
2555 errors.push(String::from("SegmentTimeline has segment@d > @maxSegmentDuration"));
2556 }
2557 }
2558 }
2559 errors
2560}
2561
2562fn check_segment_template_conformity(st: &SegmentTemplate) -> Vec<String> {
2563 let mut errors = Vec::new();
2564 if let Some(md) = &st.media {
2565 if !valid_url_p(md) {
2566 errors.push(format!("invalid URL {md}"));
2567 }
2568 if md.contains("$Number$") && md.contains("$Time") {
2569 errors.push(String::from("both $Number$ and $Time$ are used in media template URL"));
2570 }
2571 }
2572 if let Some(init) = &st.initialization {
2573 if !valid_url_p(init) {
2574 errors.push(format!("invalid URL {init}"));
2575 }
2576 if init.contains("$Number") {
2577 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2578 }
2579 if init.contains("$Time") {
2580 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2581 }
2582 }
2583 if st.duration.is_some() && st.SegmentTimeline.is_some() {
2584 errors.push(String::from("both SegmentTemplate.duration and SegmentTemplate.SegmentTimeline present"));
2585 }
2586 errors
2587}
2588
2589
2590fn valid_url_p(u: &str) -> bool {
2593 use url::ParseError;
2594
2595 match Url::parse(u) {
2596 Ok(url) => {
2597 url.scheme() == "https" ||
2598 url.scheme() == "http" ||
2599 url.scheme() == "ftp" ||
2600 url.scheme() == "file" ||
2601 url.scheme() == "data"
2602 },
2603 Err(ParseError::RelativeUrlWithoutBase) => true,
2604 Err(_) => false,
2605 }
2606}
2607
2608#[must_use]
2610pub fn check_conformity(mpd: &MPD) -> Vec<String> {
2611 let mut errors = Vec::new();
2612
2613 for p in &mpd.periods {
2616 if p.adaptations.is_empty() {
2617 errors.push(format!("Period with @id {} contains no AdaptationSet elements",
2618 p.id.clone().unwrap_or(String::from("<unspecified>"))));
2619 }
2620 for a in &p.adaptations {
2621 if let Some(mh) = a.maxHeight {
2622 if let Some(mr) = a.representations.iter().max_by_key(|r| r.height.unwrap_or(0)) {
2623 if mr.height.unwrap_or(0) > mh {
2624 errors.push(String::from("invalid @maxHeight on AdaptationSet"));
2625 }
2626 }
2627 }
2628 }
2629 }
2630 for p in &mpd.periods {
2633 for a in &p.adaptations {
2634 if let Some(mw) = a.maxWidth {
2635 if let Some(mr) = a.representations.iter().max_by_key(|r| r.width.unwrap_or(0)) {
2636 if mr.width.unwrap_or(0) > mw {
2637 errors.push(String::from("invalid @maxWidth on AdaptationSet"));
2638 }
2639 }
2640 }
2641 }
2642 }
2643 for p in &mpd.periods {
2646 for a in &p.adaptations {
2647 if let Some(mb) = a.maxBandwidth {
2648 if let Some(mr) = a.representations.iter().max_by_key(|r| r.bandwidth.unwrap_or(0)) {
2649 if mr.bandwidth.unwrap_or(0) > mb {
2650 errors.push(String::from("invalid @maxBandwidth on AdaptationSet"));
2651 }
2652 }
2653 }
2654 }
2655 }
2656 if let Some(max_seg_duration) = mpd.maxSegmentDuration {
2658 for p in &mpd.periods {
2659 for a in &p.adaptations {
2660 let mut outer_timescale = 1;
2671 if let Some(st) = &a.SegmentTemplate {
2672 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2673 .into_iter()
2674 .for_each(|msg| errors.push(msg));
2675 if let Some(ots) = st.timescale {
2676 outer_timescale = ots;
2677 }
2678 }
2679 for r in &a.representations {
2680 if let Some(st) = &r.SegmentTemplate {
2681 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2682 .into_iter()
2683 .for_each(|msg| errors.push(msg));
2684 }
2685 }
2686 }
2687 }
2688 }
2689
2690 for bu in &mpd.base_url {
2691 if !valid_url_p(&bu.base) {
2692 errors.push(format!("invalid URL {}", &bu.base));
2693 }
2694 }
2695 for p in &mpd.periods {
2696 for bu in &p.BaseURL {
2697 if !valid_url_p(&bu.base) {
2698 errors.push(format!("invalid URL {}", &bu.base));
2699 }
2700 }
2701 for a in &p.adaptations {
2702 for bu in &a.BaseURL {
2703 if !valid_url_p(&bu.base) {
2704 errors.push(format!("invalid URL {}", &bu.base));
2705 }
2706 }
2707 if let Some(st) = &a.SegmentTemplate {
2708 check_segment_template_conformity(st)
2709 .into_iter()
2710 .for_each(|msg| errors.push(msg));
2711 }
2712 for r in &a.representations {
2713 for bu in &r.BaseURL {
2714 if !valid_url_p(&bu.base) {
2715 errors.push(format!("invalid URL {}", &bu.base));
2716 }
2717 }
2718 if let Some(sb) = &r.SegmentBase {
2719 if let Some(init) = &sb.Initialization {
2720 if let Some(su) = &init.sourceURL {
2721 if !valid_url_p(su) {
2722 errors.push(format!("invalid URL {su}"));
2723 }
2724 if su.contains("$Number") {
2725 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2726 }
2727 if su.contains("$Time") {
2728 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2729 }
2730 }
2731 }
2732 if let Some(ri) = &sb.representation_index {
2733 if let Some(su) = &ri.sourceURL {
2734 if !valid_url_p(su) {
2735 errors.push(format!("invalid URL {su}"));
2736 }
2737 }
2738 }
2739 }
2740 if let Some(sl) = &r.SegmentList {
2741 if let Some(hr) = &sl.href {
2742 if !valid_url_p(hr) {
2743 errors.push(format!("invalid URL {hr}"));
2744 }
2745 }
2746 if let Some(init) = &sl.Initialization {
2747 if let Some(su) = &init.sourceURL {
2748 if !valid_url_p(su) {
2749 errors.push(format!("invalid URL {su}"));
2750 }
2751 if su.contains("$Number") {
2752 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2753 }
2754 if su.contains("$Time") {
2755 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2756 }
2757 }
2758 }
2759 for su in &sl.segment_urls {
2760 if let Some(md) = &su.media {
2761 if !valid_url_p(md) {
2762 errors.push(format!("invalid URL {md}"));
2763 }
2764 }
2765 if let Some(ix) = &su.index {
2766 if !valid_url_p(ix) {
2767 errors.push(format!("invalid URL {ix}"));
2768 }
2769 }
2770 }
2771 }
2772 if let Some(st) = &r.SegmentTemplate {
2773 check_segment_template_conformity(st)
2774 .into_iter()
2775 .for_each(|msg| errors.push(msg));
2776 }
2777 }
2778 }
2779 }
2780 for pi in &mpd.ProgramInformation {
2781 if let Some(u) = &pi.moreInformationURL {
2782 if !valid_url_p(u) {
2783 errors.push(format!("invalid URL {u}"));
2784 }
2785 }
2786 }
2787 errors
2788}
2789
2790#[cfg(test)]
2791mod tests {
2792 use proptest::prelude::*;
2793 use std::fs;
2794 use std::path::PathBuf;
2795 use std::time::Duration;
2796
2797 proptest! {
2798 #[test]
2799 fn doesnt_crash(s in "\\PC*") {
2800 let _ = super::parse_xs_duration(&s);
2801 let _ = super::parse_xs_datetime(&s);
2802 }
2803 }
2804
2805 #[test]
2806 fn test_parse_xs_duration() {
2807 use super::parse_xs_duration;
2808
2809 assert!(parse_xs_duration("").is_err());
2810 assert!(parse_xs_duration("foobles").is_err());
2811 assert!(parse_xs_duration("P").is_err());
2812 assert!(parse_xs_duration("PW").is_err());
2813 assert!(parse_xs_duration("-PT4.5S").is_err());
2815 assert!(parse_xs_duration("1Y2M3DT4H5M6S").is_err()); assert_eq!(parse_xs_duration("PT3H11M53S").ok(), Some(Duration::new(11513, 0)));
2817 assert_eq!(parse_xs_duration("PT42M30S").ok(), Some(Duration::new(2550, 0)));
2818 assert_eq!(parse_xs_duration("PT30M38S").ok(), Some(Duration::new(1838, 0)));
2819 assert_eq!(parse_xs_duration("PT0H10M0.00S").ok(), Some(Duration::new(600, 0)));
2820 assert_eq!(parse_xs_duration("PT1.5S").ok(), Some(Duration::new(1, 500_000_000)));
2821 assert_eq!(parse_xs_duration("PT1.500S").ok(), Some(Duration::new(1, 500_000_000)));
2822 assert_eq!(parse_xs_duration("PT1.500000000S").ok(), Some(Duration::new(1, 500_000_000)));
2823 assert_eq!(parse_xs_duration("PT0S").ok(), Some(Duration::new(0, 0)));
2824 assert_eq!(parse_xs_duration("PT0.001S").ok(), Some(Duration::new(0, 1_000_000)));
2825 assert_eq!(parse_xs_duration("PT0.00100S").ok(), Some(Duration::new(0, 1_000_000)));
2826 assert_eq!(parse_xs_duration("PT344S").ok(), Some(Duration::new(344, 0)));
2827 assert_eq!(parse_xs_duration("PT634.566S").ok(), Some(Duration::new(634, 566_000_000)));
2828 assert_eq!(parse_xs_duration("PT72H").ok(), Some(Duration::new(72*60*60, 0)));
2829 assert_eq!(parse_xs_duration("PT0H0M30.030S").ok(), Some(Duration::new(30, 30_000_000)));
2830 assert_eq!(parse_xs_duration("PT1004199059S").ok(), Some(Duration::new(1004199059, 0)));
2831 assert_eq!(parse_xs_duration("P0Y20M0D").ok(), Some(Duration::new(51840000, 0)));
2832 assert_eq!(parse_xs_duration("PT1M30.5S").ok(), Some(Duration::new(90, 500_000_000)));
2833 assert_eq!(parse_xs_duration("PT10M10S").ok(), Some(Duration::new(610, 0)));
2834 assert_eq!(parse_xs_duration("PT1H0.040S").ok(), Some(Duration::new(3600, 40_000_000)));
2835 assert_eq!(parse_xs_duration("PT00H03M30SZ").ok(), Some(Duration::new(210, 0)));
2836 assert_eq!(parse_xs_duration("PT3.14159S").ok(), Some(Duration::new(3, 141_590_000)));
2837 assert_eq!(parse_xs_duration("PT3.14159265S").ok(), Some(Duration::new(3, 141_592_650)));
2838 assert_eq!(parse_xs_duration("PT3.141592653S").ok(), Some(Duration::new(3, 141_592_653)));
2839 assert_eq!(parse_xs_duration("PT3.141592653897S").ok(), Some(Duration::new(3, 141_592_653)));
2841 assert_eq!(parse_xs_duration("P0W").ok(), Some(Duration::new(0, 0)));
2842 assert_eq!(parse_xs_duration("P26W").ok(), Some(Duration::new(15724800, 0)));
2843 assert_eq!(parse_xs_duration("P52W").ok(), Some(Duration::new(31449600, 0)));
2844 assert_eq!(parse_xs_duration("P10D").ok(), Some(Duration::new(864000, 0)));
2845 assert_eq!(parse_xs_duration("P0Y").ok(), Some(Duration::new(0, 0)));
2846 assert_eq!(parse_xs_duration("P1Y").ok(), Some(Duration::new(31536000, 0)));
2847 assert_eq!(parse_xs_duration("P1Y0W0S").ok(), Some(Duration::new(31536000, 0)));
2848 assert_eq!(parse_xs_duration("PT4H").ok(), Some(Duration::new(14400, 0)));
2849 assert_eq!(parse_xs_duration("+PT4H").ok(), Some(Duration::new(14400, 0)));
2850 assert_eq!(parse_xs_duration("PT0004H").ok(), Some(Duration::new(14400, 0)));
2851 assert_eq!(parse_xs_duration("PT4H0M").ok(), Some(Duration::new(14400, 0)));
2852 assert_eq!(parse_xs_duration("PT4H0S").ok(), Some(Duration::new(14400, 0)));
2853 assert_eq!(parse_xs_duration("P23DT23H").ok(), Some(Duration::new(2070000, 0)));
2854 assert_eq!(parse_xs_duration("P0Y0M0DT0H4M20.880S").ok(), Some(Duration::new(260, 880_000_000)));
2855 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6.7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2856 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6,7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2857
2858 }
2862
2863 #[test]
2864 fn test_serialize_xs_duration() {
2865 use super::MPD;
2866
2867 fn serialized_xs_duration(d: Duration) -> String {
2868 let mpd = MPD {
2869 minBufferTime: Some(d),
2870 ..Default::default()
2871 };
2872 let xml = mpd.to_string();
2873 let doc = roxmltree::Document::parse(&xml).unwrap();
2874 String::from(doc.root_element().attribute("minBufferTime").unwrap())
2875 }
2876
2877 assert_eq!("PT0S", serialized_xs_duration(Duration::new(0, 0)));
2878 assert_eq!("PT0.001S", serialized_xs_duration(Duration::new(0, 1_000_000)));
2879 assert_eq!("PT42S", serialized_xs_duration(Duration::new(42, 0)));
2880 assert_eq!("PT1.5S", serialized_xs_duration(Duration::new(1, 500_000_000)));
2881 assert_eq!("PT30.03S", serialized_xs_duration(Duration::new(30, 30_000_000)));
2882 assert_eq!("PT1M30.5S", serialized_xs_duration(Duration::new(90, 500_000_000)));
2883 assert_eq!("PT5M44S", serialized_xs_duration(Duration::new(344, 0)));
2884 assert_eq!("PT42M30S", serialized_xs_duration(Duration::new(2550, 0)));
2885 assert_eq!("PT30M38S", serialized_xs_duration(Duration::new(1838, 0)));
2886 assert_eq!("PT10M10S", serialized_xs_duration(Duration::new(610, 0)));
2887 assert_eq!("PT1H0M0.04S", serialized_xs_duration(Duration::new(3600, 40_000_000)));
2888 assert_eq!("PT3H11M53S", serialized_xs_duration(Duration::new(11513, 0)));
2889 assert_eq!("PT4H", serialized_xs_duration(Duration::new(14400, 0)));
2890 }
2891
2892 #[test]
2893 fn test_parse_xs_datetime() {
2894 use chrono::{DateTime, NaiveDate};
2895 use chrono::offset::Utc;
2896 use super::parse_xs_datetime;
2897
2898 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2899 .unwrap()
2900 .and_hms_opt(1, 3, 2)
2901 .unwrap();
2902 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02Z").ok(),
2903 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2904 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2905 .unwrap()
2906 .and_hms_nano_opt(1, 3, 2, 958*1000*1000)
2907 .unwrap();
2908 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02.958Z").ok(),
2909 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2910 }
2911
2912 #[test]
2913 fn test_parse_failure() {
2914 use super::parse;
2915
2916 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2917 path.push("tests");
2918 path.push("fixtures");
2919 path.push("incomplete.mpd");
2920 let xml = fs::read_to_string(path).unwrap();
2921 assert!(matches!(parse(&xml), Err(crate::DashMpdError::Parsing(_))));
2922 }
2923
2924 #[test]
2925 fn test_conformity_checking() {
2926 use super::{parse, check_conformity};
2927
2928 for fixture in [
2930 "a2d-tv.mpd",
2931 "ad-insertion-testcase1.mpd",
2932 "ad-insertion-testcase6-av1.mpd",
2933 "ad-insertion-testcase6-av2.mpd",
2934 "ad-insertion-testcase6-av5.mpd",
2935 "aws.xml",
2936 "dashif-live-atoinf.mpd",
2937 "dashif-low-latency.mpd",
2938 "dash-testcases-5b-1-thomson.mpd",
2939 "dolby-ac4.xml",
2940 "example_G22.mpd",
2941 "f64-inf.mpd",
2942 "jurassic-compact-5975.mpd",
2943 "mediapackage.xml",
2944 "multiple_supplementals.mpd",
2945 "orange.xml",
2946 "patch-location.mpd",
2947 "st-sl.mpd",
2948 "telenet-mid-ad-rolls.mpd",
2949 "manifest_wvcenc_1080p.mpd"] {
2950 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2951 path.push("tests");
2952 path.push("fixtures");
2953 path.push(fixture);
2954 let xml = fs::read_to_string(path)
2955 .unwrap_or_else(|_| panic!("failed to read fixture {fixture}"));
2956 let mpd = parse(&xml)
2957 .unwrap_or_else(|_| panic!("failed to parse fixture {fixture}"));
2958 let anomalies = check_conformity(&mpd);
2959 assert!(anomalies.is_empty());
2960 }
2961 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2963 path.push("tests");
2964 path.push("fixtures");
2965 path.push("admanager.xml");
2966 let xml = fs::read_to_string(path).unwrap();
2967 let mpd = parse(&xml).unwrap();
2968 let anomalies = check_conformity(&mpd);
2969 assert!(!anomalies.is_empty());
2970 for anomaly in anomalies {
2971 assert!(anomaly.starts_with("SegmentTimeline has segment@d"));
2972 }
2973 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2974 path.push("tests");
2975 path.push("fixtures");
2976 path.push( "avod-mediatailor.mpd");
2977 let xml = fs::read_to_string(path).unwrap();
2978 let mpd = parse(&xml).unwrap();
2979 let anomalies = check_conformity(&mpd);
2980 assert!(!anomalies.is_empty());
2981 for anomaly in anomalies {
2982 assert!(anomaly.starts_with("SegmentTimeline has segment@d"));
2983 }
2984 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2985 path.push("tests");
2986 path.push("fixtures");
2987 path.push("telestream-binary.xml");
2988 let xml = fs::read_to_string(path).unwrap();
2989 let mpd = parse(&xml).unwrap();
2990 let anomalies = check_conformity(&mpd);
2991 assert!(!anomalies.is_empty());
2992 for anomaly in anomalies {
2993 assert!(anomaly.starts_with("Period with @id <unspecified> contains no AdaptationSet elements"));
2994 }
2995 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2996 path.push("tests");
2997 path.push("fixtures");
2998 path.push("telestream-elements.xml");
2999 let xml = fs::read_to_string(path).unwrap();
3000 let mpd = parse(&xml).unwrap();
3001 let anomalies = check_conformity(&mpd);
3002 assert!(!anomalies.is_empty());
3003 for anomaly in anomalies {
3004 assert!(anomaly.starts_with("Period with @id <unspecified> contains no AdaptationSet elements"));
3005 }
3006 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3007 path.push("tests");
3008 path.push("fixtures");
3009 path.push("vod-aip-unif-streaming.mpd");
3010 let xml = fs::read_to_string(path).unwrap();
3011 let mpd = parse(&xml).unwrap();
3012 let anomalies = check_conformity(&mpd);
3013 assert!(!anomalies.is_empty());
3014 for anomaly in anomalies {
3015 assert!(anomaly.starts_with("SegmentTimeline has segment@d > @maxSegmentDuration"));
3016 }
3017 }
3018}