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 = "fetch")]
83pub mod stpp;
84#[cfg(feature = "fetch")]
85pub mod vtt;
86#[cfg(feature = "scte35")]
88pub mod scte35;
89#[cfg(feature = "scte35")]
90use crate::scte35::{Signal, SpliceInfoSection};
91
92#[cfg(all(feature = "fetch", feature = "libav"))]
93use crate::libav::{mux_audio_video, copy_video_to_container, copy_audio_to_container};
94#[cfg(all(feature = "fetch", not(feature = "libav")))]
95use crate::ffmpeg::{mux_audio_video, copy_video_to_container, copy_audio_to_container};
96
97#[cfg(all(feature = "sandbox", feature = "fetch", target_os = "linux"))]
98pub mod sandbox;
99
100use serde::{Serialize, Serializer, Deserialize};
101use serde::de;
102use serde_with::skip_serializing_none;
103use regex::Regex;
104use std::sync::LazyLock;
105use std::time::Duration;
106use chrono::DateTime;
107use url::Url;
108#[allow(unused_imports)]
109use tracing::warn;
110
111static XS_DURATION_REGEX: LazyLock<Regex> = LazyLock::new(||
113 Regex::new(concat!(r"^(?P<sign>[+-])?P",
114 r"(?:(?P<years>\d+)Y)?",
115 r"(?:(?P<months>\d+)M)?",
116 r"(?:(?P<weeks>\d+)W)?",
117 r"(?:(?P<days>\d+)D)?",
118 r"(?:(?P<hastime>T)", r"(?:(?P<hours>\d+)H)?",
120 r"(?:(?P<minutes>\d+)M)?",
121 r"(?:(?P<seconds>\d+)(?:(?P<nanoseconds>[.,]\d+)?)S)?",
122 r")?")).unwrap()
123);
124
125pub type XsDatetime = DateTime<chrono::offset::Utc>;
128
129#[derive(thiserror::Error, Debug)]
130#[non_exhaustive]
131pub enum DashMpdError {
132 #[error("parse error {0:?}")]
133 Parsing(String),
134 #[error("invalid Duration: {0:?}")]
135 InvalidDuration(String),
136 #[error("invalid DateTime: {0:?}")]
137 InvalidDateTime(String),
138 #[error("invalid media stream: {0:?}")]
139 UnhandledMediaStream(String),
140 #[error("I/O error {1} ({0:?})")]
141 Io(#[source] std::io::Error, String),
142 #[error("network error {0:?}")]
143 Network(String),
144 #[error("network timeout: {0:?}")]
145 NetworkTimeout(String),
146 #[error("network connection: {0:?}")]
147 NetworkConnect(String),
148 #[error("muxing error {0:?}")]
149 Muxing(String),
150 #[error("decryption error {0:?}")]
151 Decrypting(String),
152 #[error("{0:?}")]
153 Other(String),
154}
155
156
157fn serialize_xsd_double<S>(xsd: &f64, serializer: S) -> Result<S::Ok, S::Error>
162where
163 S: Serializer,
164{
165 let formatted = if xsd.is_nan() {
166 String::from("NaN")
167 } else if xsd.is_infinite() {
168 if xsd.is_sign_positive() {
169 String::from("INF")
171 } else {
172 String::from("-INF")
173 }
174 } else {
175 xsd.to_string()
176 };
177 serializer.serialize_str(&formatted)
178}
179
180#[allow(clippy::ref_option)]
182fn serialize_opt_xsd_double<S>(oxsd: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
183where
184 S: Serializer,
185{
186 if let Some(xsd) = oxsd {
187 serialize_xsd_double(xsd, serializer)
188 } else {
189 serializer.serialize_none()
191 }
192}
193
194
195fn parse_xs_duration(s: &str) -> Result<Duration, DashMpdError> {
218 use std::cmp::min;
219
220 match XS_DURATION_REGEX.captures(s) {
221 Some(m) => {
222 if m.name("hastime").is_none() &&
223 m.name("years").is_none() &&
224 m.name("months").is_none() &&
225 m.name("weeks").is_none() &&
226 m.name("days").is_none() {
227 return Err(DashMpdError::InvalidDuration("empty".to_string()));
228 }
229 let mut secs: u64 = 0;
230 let mut nsecs: u32 = 0;
231 if let Some(nano) = m.name("nanoseconds") {
232 let lim = min(nano.as_str().len(), 9 + ".".len());
235 if let Some(ss) = &nano.as_str().get(1..lim) {
236 let padded = format!("{ss:0<9}");
237 nsecs = padded.parse::<u32>()
238 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
239 }
240 }
241 if let Some(mseconds) = m.name("seconds") {
242 let seconds = mseconds.as_str().parse::<u64>()
243 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
244 secs += seconds;
245 }
246 if let Some(mminutes) = m.name("minutes") {
247 let minutes = mminutes.as_str().parse::<u64>()
248 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
249 secs += minutes * 60;
250 }
251 if let Some(mhours) = m.name("hours") {
252 let hours = mhours.as_str().parse::<u64>()
253 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
254 secs += hours * 60 * 60;
255 }
256 if let Some(mdays) = m.name("days") {
257 let days = mdays.as_str().parse::<u64>()
258 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
259 secs += days * 60 * 60 * 24;
260 }
261 if let Some(mweeks) = m.name("weeks") {
262 let weeks = mweeks.as_str().parse::<u64>()
263 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
264 secs += weeks * 60 * 60 * 24 * 7;
265 }
266 if let Some(mmonths) = m.name("months") {
267 let months = mmonths.as_str().parse::<u64>()
268 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
269 secs += months * 60 * 60 * 24 * 30;
270 }
271 if let Some(myears) = m.name("years") {
272 let years = myears.as_str().parse::<u64>()
273 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
274 secs += years * 60 * 60 * 24 * 365;
275 }
276 if let Some(msign) = m.name("sign") {
277 if msign.as_str() == "-" {
278 return Err(DashMpdError::InvalidDuration("can't represent negative durations".to_string()));
279 }
280 }
281 Ok(Duration::new(secs, nsecs))
282 },
283 None => Err(DashMpdError::InvalidDuration(String::from("couldn't parse XS duration"))),
284 }
285}
286
287
288fn deserialize_xs_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
336where
337 D: de::Deserializer<'de>,
338{
339 match <Option<String>>::deserialize(deserializer) {
340 Ok(optstring) => match optstring {
341 Some(xs) => match parse_xs_duration(&xs) {
342 Ok(d) => Ok(Some(d)),
343 Err(e) => Err(de::Error::custom(e)),
344 },
345 None => Ok(None),
346 },
347 Err(_) => Ok(None),
349 }
350}
351
352#[allow(clippy::ref_option)]
360fn serialize_xs_duration<S>(oxs: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
361where
362 S: Serializer,
363{
364 if let Some(xs) = oxs {
365 let total_secs = xs.as_secs();
366 let nanos = xs.subsec_nanos();
367 let hours = total_secs / 3600;
368 let mins = (total_secs % 3600) / 60;
369 let secs = total_secs % 60;
370 let frac = if nanos > 0 {
371 format!(".{nanos:09}").trim_end_matches('0').to_string()
372 } else {
373 String::new()
374 };
375 let s = match (hours, mins, secs, nanos) {
376 (h, 0, 0, 0) if h > 0 => format!("PT{h}H"),
377 (h, m, 0, 0) if h > 0 => format!("PT{h}H{m}M"),
378 (0, m, 0, 0) if m > 0 => format!("PT{m}M"),
379 (h, m, s, _) if h > 0 => format!("PT{h}H{m}M{s}{frac}S"),
380 (0, m, s, _) if m > 0 => format!("PT{m}M{s}{frac}S"),
381 _ => format!("PT{secs}{frac}S"),
382 };
383 serializer.serialize_str(&s)
384 } else {
385 serializer.serialize_none()
387 }
388}
389
390
391fn parse_xs_datetime(s: &str) -> Result<XsDatetime, DashMpdError> {
397 use iso8601::Date;
398 use chrono::{LocalResult, NaiveDate, TimeZone};
399 use num_traits::cast::FromPrimitive;
400 match DateTime::<chrono::offset::FixedOffset>::parse_from_rfc3339(s) {
401 Ok(dt) => Ok(dt.into()),
402 Err(_) => match iso8601::datetime(s) {
403 Ok(dt) => {
404 let nd = match dt.date {
405 Date::YMD { year, month, day } =>
406 NaiveDate::from_ymd_opt(year, month, day)
407 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
408 Date::Week { year, ww, d } => {
409 let d = chrono::Weekday::from_u32(d)
410 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
411 NaiveDate::from_isoywd_opt(year, ww, d)
412 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
413 },
414 Date::Ordinal { year, ddd } =>
415 NaiveDate::from_yo_opt(year, ddd)
416 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
417 };
418 let nd = nd.and_hms_nano_opt(dt.time.hour, dt.time.minute, dt.time.second, dt.time.millisecond*1000*1000)
419 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
420 let tz_secs = dt.time.tz_offset_hours * 3600 + dt.time.tz_offset_minutes * 60;
421 match chrono::FixedOffset::east_opt(tz_secs)
422 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
423 .from_local_datetime(&nd)
424 {
425 LocalResult::Single(local) => Ok(local.with_timezone(&chrono::Utc)),
426 _ => Err(DashMpdError::InvalidDateTime(s.to_string())),
427 }
428 },
429 Err(_) => Err(DashMpdError::InvalidDateTime(s.to_string())),
430 }
431 }
432}
433
434fn deserialize_xs_datetime<'de, D>(deserializer: D) -> Result<Option<XsDatetime>, D::Error>
436where
437 D: de::Deserializer<'de>,
438{
439 match <Option<String>>::deserialize(deserializer) {
440 Ok(optstring) => match optstring {
441 Some(xs) => match parse_xs_datetime(&xs) {
442 Ok(d) => Ok(Some(d)),
443 Err(e) => Err(de::Error::custom(e)),
444 },
445 None => Ok(None),
446 },
447 Err(_) => Ok(None),
449 }
450}
451
452fn serialize_xsd_uintvector<S>(v: &Vec<u64>, serializer: S) -> Result<S::Ok, S::Error>
455where
456 S: Serializer,
457{
458 let mut formatted = String::new();
459 for u in v {
460 formatted += &format!("{u} ");
461 }
462 serializer.serialize_str(&formatted)
463}
464
465fn deserialize_xsd_uintvector<'de, D>(deserializer: D) -> Result<Vec<u64>, D::Error>
466where
467 D: de::Deserializer<'de>,
468{
469 let s = String::deserialize(deserializer)?;
470 let mut out = Vec::<u64>::new();
471 for uint64_str in s.split_whitespace() {
472 match uint64_str.parse::<u64>() {
473 Ok(val) => out.push(val),
474 Err(e) => return Err(de::Error::custom(e)),
475 }
476 }
477 Ok(out)
478}
479
480#[allow(clippy::ref_option)]
490fn serialize_xmlns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
491where S: serde::Serializer {
492 if let Some(s) = os {
493 serializer.serialize_str(s)
494 } else {
495 serializer.serialize_str("urn:mpeg:dash:schema:mpd:2011")
496 }
497}
498
499#[allow(clippy::ref_option)]
500fn serialize_xsi_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
501where S: serde::Serializer {
502 if let Some(s) = os {
503 serializer.serialize_str(s)
504 } else {
505 serializer.serialize_str("http://www.w3.org/2001/XMLSchema-instance")
506 }
507}
508
509#[allow(clippy::ref_option)]
510fn serialize_cenc_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:mpeg:cenc:2013")
516 }
517}
518
519#[allow(clippy::ref_option)]
520fn serialize_mspr_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
521where S: serde::Serializer {
522 if let Some(s) = os {
523 serializer.serialize_str(s)
524 } else {
525 serializer.serialize_str("urn:microsoft:playready")
526 }
527}
528
529#[allow(clippy::ref_option)]
530fn serialize_xlink_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
531where S: serde::Serializer {
532 if let Some(s) = os {
533 serializer.serialize_str(s)
534 } else {
535 serializer.serialize_str("http://www.w3.org/1999/xlink")
536 }
537}
538
539#[allow(clippy::ref_option)]
540fn serialize_dvb_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
541where S: serde::Serializer {
542 if let Some(s) = os {
543 serializer.serialize_str(s)
544 } else {
545 serializer.serialize_str("urn:dvb:dash-extensions:2014-1")
546 }
547}
548
549
550#[allow(clippy::unnecessary_wraps)]
554fn default_optstring_on_request() -> Option<String> {
555 Some("onRequest".to_string())
556}
557
558#[allow(clippy::unnecessary_wraps)]
559fn default_optstring_one() -> Option<String> {
560 Some(String::from("1"))
561}
562
563#[allow(clippy::unnecessary_wraps)]
564fn default_optstring_encoder() -> Option<String> {
565 Some(String::from("encoder"))
566}
567
568#[allow(clippy::unnecessary_wraps)]
569fn default_optstring_any() -> Option<String> {
570 Some(String::from("any"))
571}
572
573#[allow(clippy::unnecessary_wraps)]
574fn default_optstring_query() -> Option<String> {
575 Some(String::from("query"))
576}
577
578#[allow(clippy::unnecessary_wraps)]
579fn default_optstring_segment() -> Option<String> {
580 Some(String::from("segment"))
581}
582
583#[allow(clippy::unnecessary_wraps)]
584fn default_optbool_true() -> Option<bool> {
585 Some(true)
586}
587
588#[allow(clippy::unnecessary_wraps)]
589fn default_optbool_false() -> Option<bool> {
590 Some(false)
591}
592
593#[allow(clippy::unnecessary_wraps)]
594fn default_optu64_zero() -> Option<u64> {
595 Some(0)
596}
597
598#[allow(clippy::unnecessary_wraps)]
599fn default_optu64_one() -> Option<u64> {
600 Some(1)
601}
602
603
604#[skip_serializing_none]
617#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
618#[serde(default)]
619pub struct Title {
620 #[serde(rename = "$text")]
621 pub content: Option<String>,
622}
623
624#[skip_serializing_none]
626#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
627#[serde(default)]
628pub struct Source {
629 #[serde(rename = "$text")]
630 pub content: Option<String>,
631}
632
633#[skip_serializing_none]
635#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
636#[serde(default)]
637pub struct Copyright {
638 #[serde(rename = "$text")]
639 pub content: Option<String>,
640}
641
642#[skip_serializing_none]
644#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
645#[serde(default)]
646pub struct ProgramInformation {
647 #[serde(rename = "@lang")]
649 pub lang: Option<String>,
650 #[serde(rename = "@moreInformationURL")]
651 pub moreInformationURL: Option<String>,
652 pub Title: Option<Title>,
653 pub Source: Option<Source>,
654 pub Copyright: Option<Copyright>,
655 #[serde(rename(serialize = "scte214:ContentIdentifier", deserialize = "ContentIdentifier"))]
656 pub scte214ContentIdentifier: Option<Scte214ContentIdentifier>,
657}
658
659#[skip_serializing_none]
663#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
664#[serde(default)]
665pub struct Scte214ContentIdentifier {
666 #[serde(rename = "@type")]
667 pub idType: Option<String>,
668 #[serde(rename = "@value")]
669 pub idValue: Option<String>,
670}
671
672#[skip_serializing_none]
674#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
675#[serde(default)]
676pub struct S {
677 #[serde(rename = "@t")]
679 pub t: Option<u64>,
680 #[serde(rename = "@n")]
681 pub n: Option<u64>,
682 #[serde(rename = "@d")]
684 pub d: u64,
685 #[serde(rename = "@r")]
688 pub r: Option<i64>,
689 #[serde(rename = "@k")]
690 pub k: Option<u64>,
691}
692
693#[skip_serializing_none]
696#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
697#[serde(default)]
698pub struct SegmentTimeline {
699 #[serde(rename = "S")]
701 pub segments: Vec<S>,
702}
703
704#[skip_serializing_none]
711#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
712#[serde(default)]
713pub struct BitstreamSwitching {
714 #[serde(rename = "@sourceURL")]
715 pub source_url: Option<String>,
716 #[serde(rename = "@range")]
717 pub range: Option<String>,
718}
719
720#[skip_serializing_none]
724#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
725#[serde(default)]
726pub struct Initialization {
727 #[serde(rename = "@sourceURL")]
728 pub sourceURL: Option<String>,
729 #[serde(rename = "@range")]
730 pub range: Option<String>,
731}
732
733#[skip_serializing_none]
734#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
735#[serde(default)]
736pub struct RepresentationIndex {
737 #[serde(rename = "@range")]
738 pub range: Option<String>,
739 #[serde(rename = "@sourceURL")]
740 pub sourceURL: Option<String>,
741}
742
743#[skip_serializing_none]
746#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
747#[serde(default)]
748pub struct SegmentTemplate {
749 #[serde(rename = "@media")]
750 pub media: Option<String>,
751 #[serde(rename = "@index")]
752 pub index: Option<String>,
753 #[serde(rename = "@initialization")]
754 pub initialization: Option<String>,
755 #[serde(rename = "@bitstreamSwitching")]
756 pub bitstreamSwitching: Option<String>,
757 #[serde(rename = "@indexRange")]
758 pub indexRange: Option<String>,
759 #[serde(rename = "@indexRangeExact")]
760 pub indexRangeExact: Option<bool>,
761 #[serde(rename = "@startNumber")]
762 pub startNumber: Option<u64>,
763 #[serde(rename = "@endNumber")]
764 pub endNumber: Option<u64>,
765 #[serde(rename = "@duration")]
769 pub duration: Option<f64>,
770 #[serde(rename = "@timescale")]
771 pub timescale: Option<u64>,
772 #[serde(rename = "@eptDelta")]
774 pub eptDelta: Option<i64>,
775 #[serde(rename = "@pdDelta")]
778 pub pbDelta: Option<i64>,
779 #[serde(rename = "@presentationTimeOffset")]
780 pub presentationTimeOffset: Option<u64>,
781 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
782 pub availabilityTimeOffset: Option<f64>,
783 #[serde(rename = "@availabilityTimeComplete")]
784 pub availabilityTimeComplete: Option<bool>,
785 pub Initialization: Option<Initialization>,
786 #[serde(rename = "RepresentationIndex")]
787 pub representation_index: Option<RepresentationIndex>,
788 #[serde(rename = "FailoverContent")]
792 pub failover_content: Option<FailoverContent>,
793 pub SegmentTimeline: Option<SegmentTimeline>,
794 pub BitstreamSwitching: Option<BitstreamSwitching>,
795}
796
797#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
801#[serde(default)]
802pub struct Location {
803 #[serde(rename = "$text")]
804 pub url: String,
805}
806
807#[skip_serializing_none]
813#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
814#[serde(default)]
815pub struct BaseURL {
816 #[serde(rename = "@serviceLocation")]
817 pub serviceLocation: Option<String>,
818 #[serde(rename = "@byteRange")]
819 pub byte_range: Option<String>,
820 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
823 pub availability_time_offset: Option<f64>,
824 #[serde(rename = "@availabilityTimeComplete")]
825 pub availability_time_complete: Option<bool>,
826 #[serde(rename = "@timeShiftBufferDepth",
827 serialize_with = "serialize_xs_duration",
828 deserialize_with = "deserialize_xs_duration",
829 default)]
830 pub timeShiftBufferDepth: Option<Duration>,
831 #[serde(rename = "@dvb:priority", alias = "@priority")]
833 pub priority: Option<u64>,
834 #[serde(rename = "@dvb:weight", alias = "@weight")]
838 pub weight: Option<i64>,
839 #[serde(rename = "$text")]
840 pub base: String,
841}
842
843#[skip_serializing_none]
849#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
850#[serde(default)]
851pub struct Fcs {
852 #[serde(rename = "@t")]
855 pub t: u64,
856
857 #[serde(rename = "@d")]
860 pub d: Option<u64>,
861}
862
863#[skip_serializing_none]
866#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
867#[serde(default)]
868pub struct FailoverContent {
869 #[serde(rename = "@valid")]
872 pub valid: Option<bool>,
873 #[serde(rename = "FCS")]
874 pub fcs_list: Vec<Fcs>,
875}
876
877#[skip_serializing_none]
879#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
880#[serde(default)]
881pub struct SegmentBase {
882 #[serde(rename = "@timescale")]
883 pub timescale: Option<u64>,
884 #[serde(rename = "@presentationTimeOffset")]
885 pub presentationTimeOffset: Option<u64>,
886 #[serde(rename = "@indexRange")]
887 pub indexRange: Option<String>,
888 #[serde(rename = "@indexRangeExact")]
889 pub indexRangeExact: Option<bool>,
890 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
891 pub availabilityTimeOffset: Option<f64>,
892 #[serde(rename = "@availabilityTimeComplete")]
893 pub availabilityTimeComplete: Option<bool>,
894 #[serde(rename = "@presentationDuration")]
895 pub presentationDuration: Option<u64>,
896 #[serde(rename = "@eptDelta")]
898 pub eptDelta: Option<i64>,
899 #[serde(rename = "@pdDelta")]
902 pub pbDelta: Option<i64>,
903 pub Initialization: Option<Initialization>,
904 #[serde(rename = "RepresentationIndex")]
905 pub representation_index: Option<RepresentationIndex>,
906 #[serde(rename = "FailoverContent")]
907 pub failover_content: Option<FailoverContent>,
908}
909
910#[skip_serializing_none]
912#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
913#[serde(default)]
914pub struct SegmentURL {
915 #[serde(rename = "@media")]
916 pub media: Option<String>, #[serde(rename = "@mediaRange")]
918 pub mediaRange: Option<String>,
919 #[serde(rename = "@index")]
920 pub index: Option<String>, #[serde(rename = "@indexRange")]
922 pub indexRange: Option<String>,
923}
924
925#[skip_serializing_none]
927#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
928#[serde(default)]
929pub struct SegmentList {
930 #[serde(rename = "@duration")]
932 pub duration: Option<u64>,
933 #[serde(rename = "@timescale")]
934 pub timescale: Option<u64>,
935 #[serde(rename = "@indexRange")]
936 pub indexRange: Option<String>,
937 #[serde(rename = "@indexRangeExact")]
938 pub indexRangeExact: Option<bool>,
939 #[serde(rename = "@xlink:href", alias = "@href")]
941 pub href: Option<String>,
942 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
943 pub actuate: Option<String>,
944 #[serde(rename = "@xlink:type", alias = "@type")]
945 pub sltype: Option<String>,
946 #[serde(rename = "@xlink:show", alias = "@show")]
947 pub show: Option<String>,
948 pub Initialization: Option<Initialization>,
949 pub SegmentTimeline: Option<SegmentTimeline>,
950 pub BitstreamSwitching: Option<BitstreamSwitching>,
951 #[serde(rename = "SegmentURL")]
952 pub segment_urls: Vec<SegmentURL>,
953}
954
955#[skip_serializing_none]
956#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
957#[serde(default)]
958pub struct Resync {
959 #[serde(rename = "@type")]
960 pub rtype: Option<String>,
961 #[serde(rename = "@dT")]
962 pub dT: Option<u64>,
963 #[serde(rename = "@dImax")]
964 pub dImax: Option<f64>,
965 #[serde(rename = "@dImin")]
966 pub dImin: Option<f64>,
967 #[serde(rename = "@marker")]
968 pub marker: Option<bool>,
969}
970
971#[skip_serializing_none]
973#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
974#[serde(default)]
975pub struct AudioChannelConfiguration {
976 #[serde(rename = "@schemeIdUri")]
977 pub schemeIdUri: String,
978 #[serde(rename = "@value")]
979 pub value: Option<String>,
980 #[serde(rename = "@id")]
981 pub id: Option<String>,
982}
983
984#[skip_serializing_none]
986#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
987#[serde(default)]
988pub struct Language {
989 #[serde(rename = "$text")]
990 pub content: Option<String>,
991}
992
993#[skip_serializing_none]
999#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1000#[serde(default)]
1001pub struct Preselection {
1002 #[serde(rename = "@id", default = "default_optstring_one")]
1003 pub id: Option<String>,
1004 #[serde(rename = "@preselectionComponents")]
1007 pub preselectionComponents: String,
1008 #[serde(rename = "@lang")]
1009 pub lang: Option<String>,
1010 #[serde(rename = "@audioSamplingRate")]
1011 pub audioSamplingRate: Option<String>,
1012 #[serde(rename = "@codecs")]
1014 pub codecs: String,
1015 #[serde(rename = "@selectionPriority")]
1016 pub selectionPriority: Option<u64>,
1017 #[serde(rename = "@tag")]
1018 pub tag: String,
1019 pub FramePacking: Vec<FramePacking>,
1020 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1021 pub ContentProtection: Vec<ContentProtection>,
1022 pub OutputProtection: Option<OutputProtection>,
1023 #[serde(rename = "EssentialProperty")]
1024 pub essential_property: Vec<EssentialProperty>,
1025 #[serde(rename = "SupplementalProperty")]
1026 pub supplemental_property: Vec<SupplementalProperty>,
1027 pub InbandEventStream: Vec<InbandEventStream>,
1028 pub Switching: Vec<Switching>,
1029 #[serde(rename = "GroupLabel")]
1031 pub group_label: Vec<Label>,
1032 pub Label: Vec<Label>,
1033 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
1034 pub Resync: Option<Resync>,
1036 #[serde(rename = "Accessibility")]
1037 pub accessibilities: Vec<Accessibility>,
1038 #[serde(rename = "Role")]
1039 pub roles: Vec<Role>,
1040 #[serde(rename = "Rating")]
1041 pub ratings: Vec<Rating>,
1042 #[serde(rename = "Viewpoint")]
1043 pub viewpoints: Vec<Viewpoint>,
1044 #[serde(rename = "Language")]
1046 pub languages: Vec<Language>,
1047}
1048
1049#[skip_serializing_none]
1052#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1053#[serde(default)]
1054pub struct Rating {
1055 #[serde(rename = "@id")]
1056 pub id: Option<String>,
1057 #[serde(rename = "@schemeIdUri")]
1058 pub schemeIdUri: String,
1059 #[serde(rename = "@value")]
1060 pub value: Option<String>,
1061}
1062
1063#[skip_serializing_none]
1065#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1066#[serde(default)]
1067pub struct FramePacking {
1068 #[serde(rename = "@id")]
1069 pub id: Option<String>,
1070 #[serde(rename = "@schemeIdUri")]
1071 pub schemeIdUri: String,
1072 #[serde(rename = "@value")]
1073 pub value: Option<String>,
1074}
1075
1076#[skip_serializing_none]
1081#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1082#[serde(default)]
1083pub struct Switching {
1084 #[serde(rename = "@interval")]
1085 pub interval: Option<u64>,
1086 #[serde(rename = "@type")]
1088 pub stype: Option<String>,
1089}
1090
1091#[skip_serializing_none]
1093#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1094#[serde(default)]
1095pub struct Accessibility {
1096 #[serde(rename = "@schemeIdUri")]
1097 pub schemeIdUri: String,
1098 #[serde(rename = "@value")]
1099 pub value: Option<String>,
1100 #[serde(rename = "@id")]
1101 pub id: Option<String>,
1102}
1103
1104#[skip_serializing_none]
1106#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1107#[serde(default)]
1108pub struct Scope {
1109 #[serde(rename = "@schemeIdUri")]
1110 pub schemeIdUri: String,
1111 #[serde(rename = "@value")]
1112 pub value: Option<String>,
1113 #[serde(rename = "@id")]
1114 pub id: Option<String>,
1115}
1116
1117#[skip_serializing_none]
1119#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1120#[serde(default)]
1121pub struct SubRepresentation {
1122 #[serde(rename = "@level")]
1123 pub level: Option<u32>,
1124 #[serde(rename = "@dependencyLevel")]
1125 pub dependencyLevel: Option<String>,
1126 #[serde(rename = "@contentComponent")]
1128 pub contentComponent: Option<String>,
1129 #[serde(rename = "@mimeType")]
1130 pub mimeType: Option<String>,
1131 #[serde(rename = "@codecs")]
1133 pub codecs: Option<String>,
1134 #[serde(rename = "@contentType")]
1135 pub contentType: Option<String>,
1136 #[serde(rename = "@profiles")]
1137 pub profiles: Option<String>,
1138 #[serde(rename = "@segmentProfiles")]
1139 pub segmentProfiles: Option<String>,
1142 #[serde(rename = "@scanType")]
1144 pub scanType: Option<String>,
1145 #[serde(rename = "@frameRate")]
1146 pub frameRate: Option<String>, #[serde(rename = "@sar")]
1149 pub sar: Option<String>,
1150 #[serde(rename = "@bandwidth")]
1152 pub bandwidth: Option<u64>,
1153 #[serde(rename = "@audioSamplingRate")]
1154 pub audioSamplingRate: Option<String>,
1155 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1157 pub maxPlayoutRate: Option<f64>,
1158 #[serde(rename = "@codingDependency")]
1159 pub codingDependency: Option<bool>,
1160 #[serde(rename = "@width")]
1161 pub width: Option<u64>,
1162 #[serde(rename = "@height")]
1163 pub height: Option<u64>,
1164 #[serde(rename = "@startWithSAP")]
1165 pub startWithSAP: Option<u64>,
1166 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1167 pub maximumSAPPeriod: Option<f64>,
1168 pub FramePacking: Vec<FramePacking>,
1169 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1170 pub ContentProtection: Vec<ContentProtection>,
1171 pub OutputProtection: Option<OutputProtection>,
1172 #[serde(rename = "EssentialProperty")]
1173 pub essential_property: Vec<EssentialProperty>,
1174 #[serde(rename = "SupplementalProperty")]
1175 pub supplemental_property: Vec<SupplementalProperty>,
1176 pub InbandEventStream: Vec<InbandEventStream>,
1177 pub Switching: Vec<Switching>,
1178 #[serde(rename = "GroupLabel")]
1180 pub group_label: Vec<Label>,
1181 pub Label: Vec<Label>,
1182 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
1183 pub Resync: Option<Resync>,
1185}
1186
1187#[skip_serializing_none]
1192#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1193#[serde(default)]
1194pub struct Representation {
1195 #[serde(rename = "@id")]
1197 pub id: Option<String>,
1198 #[serde(rename = "@bandwidth")]
1200 pub bandwidth: Option<u64>,
1201 #[serde(rename = "@qualityRanking")]
1205 pub qualityRanking: Option<u8>,
1206 #[serde(rename = "@dependencyId")]
1210 pub dependencyId: Option<String>,
1211 #[serde(rename = "@associationId")]
1212 pub associationId: Option<String>,
1213 #[serde(rename = "@associationType")]
1214 pub associationType: Option<String>,
1215 #[serde(rename = "@mediaStreamStructureId")]
1216 pub mediaStreamStructureId: Option<String>,
1217 #[serde(rename = "@profiles")]
1218 pub profiles: Option<String>,
1219 #[serde(rename = "@width")]
1220 pub width: Option<u64>,
1221 #[serde(rename = "@height")]
1222 pub height: Option<u64>,
1223 #[serde(rename = "@sar")]
1225 pub sar: Option<String>,
1226 #[serde(rename = "@frameRate")]
1227 pub frameRate: Option<String>, #[serde(rename = "@audioSamplingRate")]
1229 pub audioSamplingRate: Option<String>,
1230 #[serde(rename = "@mimeType")]
1233 pub mimeType: Option<String>,
1234 #[serde(rename = "@segmentProfiles")]
1237 pub segmentProfiles: Option<String>,
1238 #[serde(rename = "@codecs")]
1241 pub codecs: Option<String>,
1242 #[serde(rename = "@containerProfiles")]
1243 pub containerProfiles: Option<String>,
1244 #[serde(rename = "@maximumSAPPeriod")]
1245 pub maximumSAPPeriod: Option<f64>,
1246 #[serde(rename = "@startWithSAP")]
1247 pub startWithSAP: Option<u64>,
1248 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1250 pub maxPlayoutRate: Option<f64>,
1251 #[serde(rename = "@codingDependency")]
1252 pub codingDependency: Option<bool>,
1253 #[serde(rename = "@scanType")]
1255 pub scanType: Option<String>,
1256 #[serde(rename = "@selectionPriority")]
1257 pub selectionPriority: Option<u64>,
1258 #[serde(rename = "@tag")]
1259 pub tag: Option<String>,
1260 #[serde(rename = "@contentType")]
1261 pub contentType: Option<String>,
1262 #[serde(rename = "@lang")]
1264 pub lang: Option<String>,
1265 #[serde(rename = "@sampleRate")]
1266 pub sampleRate: Option<u64>,
1267 #[serde(rename = "@numChannels")]
1268 pub numChannels: Option<u32>,
1269 #[serde(rename = "@xlink:href", alias = "@href")]
1270 pub href: Option<String>,
1271 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1272 pub actuate: Option<String>,
1273 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1274 pub scte214_supplemental_profiles: Option<String>,
1275 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1276 pub scte214_supplemental_codecs: Option<String>,
1277 pub FramePacking: Vec<FramePacking>,
1278 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1279 pub ContentProtection: Vec<ContentProtection>,
1280 pub OutputProtection: Option<OutputProtection>,
1281 #[serde(rename = "EssentialProperty")]
1282 pub essential_property: Vec<EssentialProperty>,
1283 #[serde(rename = "SupplementalProperty")]
1284 pub supplemental_property: Vec<SupplementalProperty>,
1285 pub InbandEventStream: Vec<InbandEventStream>,
1286 pub Switching: Vec<Switching>,
1287 #[serde(rename = "GroupLabel")]
1289 pub group_label: Vec<Label>,
1290 pub Label: Vec<Label>,
1291 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1292 pub Resync: Vec<Resync>,
1294 pub BaseURL: Vec<BaseURL>,
1295 pub SubRepresentation: Vec<SubRepresentation>,
1297 pub SegmentBase: Option<SegmentBase>,
1298 pub SegmentList: Option<SegmentList>,
1299 pub SegmentTemplate: Option<SegmentTemplate>,
1300 #[serde(rename = "RepresentationIndex")]
1301 pub representation_index: Option<RepresentationIndex>,
1302}
1303
1304#[skip_serializing_none]
1306#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1307#[serde(default)]
1308pub struct ContentComponent {
1309 #[serde(rename = "@id")]
1310 pub id: Option<String>,
1311 #[serde(rename = "@lang")]
1313 pub lang: Option<String>,
1314 #[serde(rename = "@contentType")]
1315 pub contentType: Option<String>,
1316 #[serde(rename = "@par")]
1317 pub par: Option<String>,
1318 #[serde(rename = "@tag")]
1319 pub tag: Option<String>,
1320 pub Accessibility: Vec<Accessibility>,
1321 pub Role: Vec<Role>,
1322 pub Rating: Vec<Rating>,
1323 pub Viewpoint: Vec<Viewpoint>,
1324}
1325
1326#[skip_serializing_none]
1328#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1329#[serde(default)]
1330pub struct CencPssh {
1331 #[serde(rename = "$text")]
1332 pub content: Option<String>,
1333}
1334
1335#[skip_serializing_none]
1337#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1338#[serde(default)]
1339pub struct Laurl {
1340 #[serde(rename = "@Lic_type")]
1341 pub lic_type: Option<String>,
1342 #[serde(rename = "$text")]
1343 pub content: Option<String>,
1344}
1345
1346#[skip_serializing_none]
1348#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1349#[serde(default)]
1350pub struct MsprPro {
1351 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
1352 pub xmlns: Option<String>,
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 MsprIsEncrypted {
1361 #[serde(rename = "$text")]
1362 pub content: Option<String>,
1363}
1364
1365#[skip_serializing_none]
1366#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1367#[serde(default)]
1368pub struct MsprIVSize {
1369 #[serde(rename = "$text")]
1370 pub content: Option<String>,
1371}
1372
1373#[skip_serializing_none]
1374#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1375#[serde(default)]
1376pub struct MsprKid {
1377 #[serde(rename = "$text")]
1378 pub content: Option<String>,
1379}
1380
1381#[skip_serializing_none]
1382#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1383#[serde(default)]
1384pub struct OutputProtection {
1385 #[serde(rename = "@schemeIdUri")]
1386 pub schemeIdUri: String,
1387 #[serde(rename = "@value")]
1388 pub value: Option<String>,
1389 #[serde(rename = "@id")]
1390 pub id: Option<String>,
1391}
1392
1393#[skip_serializing_none]
1398#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1399#[serde(default)]
1400pub struct ContentProtection {
1401 #[serde(rename = "@robustness")]
1403 pub robustness: Option<String>,
1404 #[serde(rename = "@refId")]
1405 pub refId: Option<String>,
1406 #[serde(rename = "@ref")]
1408 pub r#ref: Option<String>,
1409 #[serde(rename = "@schemeIdUri")]
1411 pub schemeIdUri: String,
1412 #[serde(rename = "@value")]
1413 pub value: Option<String>,
1414 #[serde(rename = "@id")]
1415 pub id: Option<String>,
1416 #[serde(rename="cenc:pssh", alias="pssh")]
1418 pub cenc_pssh: Vec<CencPssh>,
1419 #[serde(rename = "@cenc:default_KID", alias = "@default_KID")]
1421 pub default_KID: Option<String>,
1422 #[serde(rename = "dashif:laurl", alias = "laurl")]
1424 pub laurl: Option<Laurl>,
1425 #[serde(rename = "clearkey:Laurl", alias = "Laurl")]
1429 pub clearkey_laurl: Option<Laurl>,
1430 #[serde(rename = "mspr:pro", alias = "pro")]
1432 pub msprpro: Option<MsprPro>,
1433 #[serde(rename = "mspr:IsEncrypted", alias = "IsEncrypted")]
1434 pub mspr_is_encrypted: Option<MsprIsEncrypted>,
1435 #[serde(rename = "mspr:IV_Size", alias = "IV_Size")]
1436 pub mspr_iv_size: Option<MsprIVSize>,
1437 #[serde(rename = "mspr:kid", alias = "kid")]
1438 pub mspr_kid: Option<MsprKid>,
1439}
1440
1441#[skip_serializing_none]
1447#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1448#[serde(default)]
1449pub struct Role {
1450 #[serde(rename = "@id")]
1451 pub id: Option<String>,
1452 #[serde(rename = "@schemeIdUri")]
1453 pub schemeIdUri: String,
1454 #[serde(rename = "@value")]
1455 pub value: Option<String>,
1456}
1457
1458#[skip_serializing_none]
1459#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1460#[serde(default)]
1461pub struct Viewpoint {
1462 #[serde(rename = "@id")]
1463 pub id: Option<String>,
1464 #[serde(rename = "@schemeIdUri")]
1465 pub schemeIdUri: String,
1466 #[serde(rename = "@value")]
1467 pub value: Option<String>,
1468}
1469
1470#[skip_serializing_none]
1471#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1472#[serde(default)]
1473pub struct Selection {
1474 #[serde(rename = "@dataEncoding")]
1475 pub dataEncoding: Option<String>,
1476 #[serde(rename = "@parameter")]
1477 pub parameter: Option<String>,
1478 #[serde(rename = "@data")]
1479 pub data: Option<String>,
1480}
1481
1482#[skip_serializing_none]
1483#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1484#[serde(default)]
1485pub struct SelectionInfo {
1486 #[serde(rename = "@selectionInfo")]
1487 pub selectionInfo: Option<String>,
1488 #[serde(rename = "@contactURL")]
1489 pub contactURL: Option<String>,
1490 pub Selection: Vec<Selection>,
1491}
1492
1493#[skip_serializing_none]
1500#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1501#[serde(default)]
1502pub struct Event {
1503 #[serde(rename = "@id")]
1504 pub id: Option<String>,
1505 #[serde(rename = "@presentationTime", default = "default_optu64_zero")]
1506 pub presentationTime: Option<u64>,
1507 #[serde(rename = "@presentationTimeOffset")]
1508 pub presentationTimeOffset: Option<u64>,
1509 #[serde(rename = "@duration")]
1510 pub duration: Option<u64>,
1511 #[serde(rename = "@timescale")]
1512 pub timescale: Option<u64>,
1513 #[serde(rename = "@contentEncoding")]
1516 pub contentEncoding: Option<String>,
1517 #[serde(rename = "@messageData")]
1520 pub messageData: Option<String>,
1521 pub SelectionInfo: Option<SelectionInfo>,
1522 #[cfg(feature = "scte35")]
1523 #[serde(rename = "scte35:Signal", alias="Signal")]
1524 #[cfg(feature = "scte35")]
1525 pub signal: Vec<Signal>,
1526 #[cfg(feature = "scte35")]
1527 #[serde(rename = "scte35:SpliceInfoSection", alias="SpliceInfoSection")]
1528 #[cfg(feature = "scte35")]
1529 pub splice_info_section: Vec<SpliceInfoSection>,
1530 #[serde(rename = "@value")]
1533 pub value: Option<String>,
1534 #[serde(rename = "$text")]
1537 pub content: Option<String>,
1538}
1539
1540#[skip_serializing_none]
1541#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1542#[serde(default)]
1543pub struct EventStream {
1544 #[serde(rename = "@xlink:href")]
1545 #[serde(alias = "@href")]
1546 pub href: Option<String>,
1547 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1548 pub actuate: Option<String>,
1549 #[serde(rename = "@messageData")]
1550 pub messageData: Option<String>,
1552 #[serde(rename = "@schemeIdUri")]
1553 pub schemeIdUri: String,
1554 #[serde(rename = "@value")]
1555 pub value: Option<String>,
1556 #[serde(rename = "@timescale")]
1557 pub timescale: Option<u64>,
1558 #[serde(rename = "@presentationTimeOffset")]
1559 pub presentationTimeOffset: Option<u64>,
1560 #[serde(rename = "Event")]
1561 pub event: Vec<Event>,
1562}
1563
1564#[skip_serializing_none]
1570#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1571#[serde(default)]
1572pub struct InbandEventStream {
1573 #[serde(rename = "@timescale")]
1574 pub timescale: Option<u64>,
1575 #[serde(rename = "@schemeIdUri")]
1576 pub schemeIdUri: String,
1577 #[serde(rename = "Event")]
1578 pub event: Vec<Event>,
1579 #[serde(rename = "@value")]
1580 pub value: Option<String>,
1581 #[serde(rename = "@xlink:href")]
1583 #[serde(alias = "@href")]
1584 pub href: Option<String>,
1585 #[serde(rename = "@xlink:actuate", alias = "@actuate")]
1586 pub actuate: Option<String>,
1587}
1588
1589#[skip_serializing_none]
1590#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1591#[serde(default)]
1592pub struct EssentialProperty {
1593 #[serde(rename = "@id")]
1594 pub id: Option<String>,
1595 #[serde(rename = "@schemeIdUri")]
1596 pub schemeIdUri: String,
1597 #[serde(rename = "@value")]
1598 pub value: Option<String>,
1599}
1600
1601#[skip_serializing_none]
1602#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1603#[serde(default)]
1604pub struct SupplementalProperty {
1605 #[serde(rename = "@id")]
1606 pub id: Option<String>,
1607 #[serde(rename = "@schemeIdUri")]
1608 pub schemeIdUri: String,
1609 #[serde(rename = "@value")]
1610 pub value: Option<String>,
1611 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1612 #[serde(rename(deserialize = "ContentIdentifier"))]
1613 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1614}
1615
1616#[skip_serializing_none]
1619#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1620#[serde(default)]
1621pub struct Label {
1622 #[serde(rename = "@id")]
1623 pub id: Option<String>,
1624 #[serde(rename = "@lang")]
1625 pub lang: Option<String>,
1626 #[serde(rename = "$text")]
1627 pub content: String,
1628}
1629
1630#[skip_serializing_none]
1638#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1639#[serde(default)]
1640pub struct AdaptationSet {
1641 #[serde(rename = "@id")]
1642 pub id: Option<String>,
1643 #[serde(rename = "@xlink:href", alias = "@href")]
1645 pub href: Option<String>,
1646 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1647 pub actuate: Option<String>,
1648 #[serde(rename = "@group")]
1649 pub group: Option<i64>,
1650 #[serde(rename = "@selectionPriority")]
1651 pub selectionPriority: Option<u64>,
1652 #[serde(rename = "@contentType")]
1654 pub contentType: Option<String>,
1655 #[serde(rename = "@profiles")]
1656 pub profiles: Option<String>,
1657 #[serde(rename = "@lang")]
1659 pub lang: Option<String>,
1660 #[serde(rename = "@sar")]
1662 pub sar: Option<String>,
1663 #[serde(rename = "@par")]
1665 pub par: Option<String>,
1666 #[serde(rename = "@scanType")]
1668 pub scanType: Option<String>,
1669 #[serde(rename = "@segmentAlignment")]
1670 pub segmentAlignment: Option<bool>,
1671 #[serde(rename = "@segmentProfiles")]
1672 pub segmentProfiles: Option<String>,
1675 #[serde(rename = "@subsegmentAlignment")]
1676 pub subsegmentAlignment: Option<bool>,
1677 #[serde(rename = "@subsegmentStartsWithSAP")]
1678 pub subsegmentStartsWithSAP: Option<u64>,
1679 #[serde(rename = "@bitstreamSwitching")]
1680 pub bitstreamSwitching: Option<bool>,
1681 #[serde(rename = "@audioSamplingRate")]
1682 pub audioSamplingRate: Option<String>,
1683 #[serde(rename = "@width")]
1684 pub width: Option<u64>,
1685 #[serde(rename = "@height")]
1686 pub height: Option<u64>,
1687 #[serde(rename = "@mimeType")]
1689 pub mimeType: Option<String>,
1690 #[serde(rename = "@codecs")]
1692 pub codecs: Option<String>,
1693 #[serde(rename = "@minBandwidth")]
1694 pub minBandwidth: Option<u64>,
1695 #[serde(rename = "@maxBandwidth")]
1696 pub maxBandwidth: Option<u64>,
1697 #[serde(rename = "@minWidth")]
1698 pub minWidth: Option<u64>,
1699 #[serde(rename = "@maxWidth")]
1700 pub maxWidth: Option<u64>,
1701 #[serde(rename = "@minHeight")]
1702 pub minHeight: Option<u64>,
1703 #[serde(rename = "@maxHeight")]
1704 pub maxHeight: Option<u64>,
1705 #[serde(rename = "@frameRate")]
1706 pub frameRate: Option<String>, #[serde(rename = "@minFrameRate")]
1708 pub minFrameRate: Option<String>, #[serde(rename = "@maxFrameRate")]
1710 pub maxFrameRate: Option<String>, #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1713 pub maxPlayoutRate: Option<f64>,
1714 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1715 pub maximumSAPPeriod: Option<f64>,
1716 #[serde(rename = "@startWithSAP")]
1717 pub startWithSAP: Option<u64>,
1718 #[serde(rename = "@codingDependency")]
1719 pub codingDependency: Option<bool>,
1720 pub FramePacking: Vec<FramePacking>,
1721 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1722 pub ContentProtection: Vec<ContentProtection>,
1723 #[serde(rename = "EssentialProperty")]
1725 pub essential_property: Vec<EssentialProperty>,
1726 #[serde(rename = "SupplementalProperty")]
1727 pub supplemental_property: Vec<SupplementalProperty>,
1728 pub InbandEventStream: Vec<InbandEventStream>,
1729 pub Switching: Vec<Switching>,
1730 pub GroupLabel: Vec<Label>,
1732 pub Label: Vec<Label>,
1733 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1734 pub Resync: Vec<Resync>,
1736 pub Accessibility: Vec<Accessibility>,
1737 pub Role: Vec<Role>,
1738 pub Rating: Vec<Rating>,
1739 pub Viewpoint: Vec<Viewpoint>,
1740 pub ContentComponent: Vec<ContentComponent>,
1741 pub BaseURL: Vec<BaseURL>,
1742 pub SegmentBase: Option<SegmentBase>,
1743 pub SegmentList: Option<SegmentList>,
1744 pub SegmentTemplate: Option<SegmentTemplate>,
1745 #[serde(rename = "Representation")]
1746 pub representations: Vec<Representation>,
1747 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1748 pub scte214_supplemental_profiles: Option<String>,
1749 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1750 pub scte214_supplemental_codecs: Option<String>,
1751}
1752
1753#[skip_serializing_none]
1758#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1759#[serde(default)]
1760pub struct AssetIdentifier {
1761 #[serde(rename = "@schemeIdUri")]
1762 pub schemeIdUri: String,
1763 #[serde(rename = "@value")]
1764 pub value: Option<String>,
1765 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1766 #[serde(rename(deserialize = "ContentIdentifier"))]
1767 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1768}
1769
1770#[skip_serializing_none]
1775#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1776#[serde(default)]
1777pub struct Subset {
1778 #[serde(rename = "@id")]
1779 pub id: Option<String>,
1780 #[serde(rename = "@contains",
1783 deserialize_with = "deserialize_xsd_uintvector",
1784 serialize_with = "serialize_xsd_uintvector",
1785 default)]
1786 pub contains: Vec<u64>,
1787}
1788
1789#[skip_serializing_none]
1792#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1793#[serde(default)]
1794pub struct Period {
1795 #[serde(rename = "@xlink:href", alias = "@href")]
1797 pub href: Option<String>,
1798
1799 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
1800 pub actuate: Option<String>,
1801
1802 #[serde(rename = "@id")]
1803 pub id: Option<String>,
1804
1805 #[serde(rename = "@start",
1807 serialize_with = "serialize_xs_duration",
1808 deserialize_with = "deserialize_xs_duration",
1809 default)]
1810 pub start: Option<Duration>,
1811
1812 #[serde(rename = "@duration",
1814 serialize_with = "serialize_xs_duration",
1815 deserialize_with = "deserialize_xs_duration",
1816 default)]
1817 pub duration: Option<Duration>,
1818
1819 #[serde(rename = "@bitstreamSwitching", default)]
1821 pub bitstreamSwitching: Option<bool>,
1822
1823 pub BaseURL: Vec<BaseURL>,
1824
1825 pub SegmentBase: Option<SegmentBase>,
1826
1827 pub SegmentList: Option<SegmentList>,
1828
1829 pub SegmentTemplate: Option<SegmentTemplate>,
1830
1831 #[serde(rename = "AssetIdentifier")]
1832 pub asset_identifier: Option<AssetIdentifier>,
1833
1834 #[serde(rename = "EventStream")]
1835 pub event_streams: Vec<EventStream>,
1836
1837 #[serde(rename = "ServiceDescription")]
1838 pub service_description: Vec<ServiceDescription>,
1839
1840 pub ContentProtection: Vec<ContentProtection>,
1841
1842 #[serde(rename = "AdaptationSet")]
1843 pub adaptations: Vec<AdaptationSet>,
1844
1845 #[serde(rename = "Subset")]
1846 pub subsets: Vec<Subset>,
1847
1848 #[serde(rename = "SupplementalProperty")]
1849 pub supplemental_property: Vec<SupplementalProperty>,
1850
1851 #[serde(rename = "EmptyAdaptationSet")]
1852 pub empty_adaptations: Vec<AdaptationSet>,
1853
1854 #[serde(rename = "GroupLabel")]
1855 pub group_label: Vec<Label>,
1856
1857 #[serde(rename = "Preselection")]
1858 pub pre_selections: Vec<Preselection>,
1859
1860 #[serde(rename = "EssentialProperty")]
1861 pub essential_property: Vec<EssentialProperty>,
1862}
1863
1864#[skip_serializing_none]
1865#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1866#[serde(default)]
1867pub struct Reporting {
1868 #[serde(rename = "@id")]
1869 pub id: Option<String>,
1870 #[serde(rename = "@schemeIdUri")]
1871 pub schemeIdUri: String,
1872 #[serde(rename = "@value")]
1873 pub value: Option<String>,
1874 #[serde(rename = "@dvb:reportingUrl", alias = "@reportingUrl")]
1875 pub reportingUrl: Option<String>,
1876 #[serde(rename = "@dvb:probability", alias = "@probability")]
1877 pub probability: Option<u64>,
1878}
1879
1880#[skip_serializing_none]
1881#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1882#[serde(default)]
1883pub struct Range {
1884 #[serde(rename = "@starttime",
1885 serialize_with = "serialize_xs_duration",
1886 deserialize_with = "deserialize_xs_duration",
1887 default)]
1888 pub starttime: Option<Duration>,
1889 #[serde(rename = "@duration",
1890 serialize_with = "serialize_xs_duration",
1891 deserialize_with = "deserialize_xs_duration",
1892 default)]
1893 pub duration: Option<Duration>,
1894}
1895
1896#[skip_serializing_none]
1897#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1898#[serde(default)]
1899pub struct Metrics {
1900 #[serde(rename = "@metrics")]
1901 pub metrics: String,
1902 pub Reporting: Vec<Reporting>,
1903 pub Range: Vec<Range>,
1904}
1905
1906#[skip_serializing_none]
1908#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1909#[serde(default)]
1910pub struct Latency {
1911 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1912 pub min: Option<f64>,
1913 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1914 pub max: Option<f64>,
1915 #[serde(rename = "@target", serialize_with="serialize_opt_xsd_double")]
1916 pub target: Option<f64>,
1917 #[serde(rename = "@referenceId")]
1918 pub referenceId: Option<String>,
1919}
1920
1921#[skip_serializing_none]
1923#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1924#[serde(default)]
1925pub struct PlaybackRate {
1926 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1927 pub min: Option<f64>,
1928 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1929 pub max: Option<f64>,
1930}
1931
1932#[skip_serializing_none]
1934#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1935#[serde(default)]
1936pub struct OperatingQuality {
1937 #[serde(default = "default_optstring_any")]
1938 pub mediaType: Option<String>,
1939 #[serde(rename = "@min")]
1940 pub min: Option<u64>,
1941 #[serde(rename = "@max")]
1942 pub max: Option<u64>,
1943 #[serde(rename = "@target")]
1944 pub target: Option<u64>,
1945 #[serde(rename = "@type")]
1946 pub _type: Option<String>,
1947 #[serde(rename = "@maxDifference")]
1948 pub maxDifference: Option<u64>,
1949}
1950
1951#[skip_serializing_none]
1953#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1954#[serde(default)]
1955pub struct OperatingBandwidth {
1956 #[serde(rename = "@mediaType", default = "default_optstring_any")]
1957 pub mediaType: Option<String>,
1958 #[serde(rename = "@min")]
1959 pub min: Option<u64>,
1960 #[serde(rename = "@max")]
1961 pub max: Option<u64>,
1962 #[serde(rename = "@target")]
1963 pub target: Option<u64>,
1964}
1965
1966#[skip_serializing_none]
1967#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1968#[serde(default)]
1969pub struct ContentSteering {
1970 #[serde(rename = "@defaultServiceLocation")]
1971 pub defaultServiceLocation: Option<String>,
1972 #[serde(rename = "@queryBeforeStart", default = "default_optbool_false")]
1973 pub queryBeforeStart: Option<bool>,
1974 #[serde(rename = "@clientRequirement", default = "default_optbool_true")]
1975 pub clientRequirement: Option<bool>,
1976}
1977
1978#[skip_serializing_none]
1979#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1980#[serde(default)]
1981pub struct CMCDParameters {
1982 #[serde(rename = "@version", default = "default_optu64_one")]
1983 pub version: Option<u64>,
1984 #[serde(rename = "@mode", default = "default_optstring_query")]
1985 pub mode: Option<String>,
1986 #[serde(rename = "@includeInRequests", default = "default_optstring_segment")]
1987 pub includeInRequests: Option<String>,
1988 #[serde(rename = "@keys")]
1989 pub keys: String,
1990 #[serde(rename = "@contentID")]
1991 pub contentID: Option<String>,
1992 #[serde(rename = "@sessionID")]
1993 pub sessionID: Option<String>,
1994}
1995
1996#[skip_serializing_none]
1998#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1999#[serde(default)]
2000pub struct ClientDataReporting {
2001 pub CMCDParameters: Vec<CMCDParameters>,
2002 #[serde(rename = "@serviceLocations")]
2003 pub serviceLocations: Option<String>,
2004 #[serde(rename = "@adaptationSets")]
2005 pub adaptationSets: Option<String>,
2006}
2007
2008#[skip_serializing_none]
2009#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2010#[serde(default)]
2011pub struct PlaybackRestrictions {
2012 #[serde(rename = "@skipAfter",
2013 serialize_with = "serialize_xs_duration",
2014 deserialize_with = "deserialize_xs_duration",
2015 default)]
2016 pub skipAfter: Option<Duration>,
2017}
2018
2019#[skip_serializing_none]
2020#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2021#[serde(default)]
2022pub struct ServiceDescription {
2023 #[serde(rename = "Scope")]
2024 pub scopes: Vec<Scope>,
2025 pub Latency: Vec<Latency>,
2026 pub PlaybackRate: Vec<PlaybackRate>,
2027 pub OperatingQuality: Vec<OperatingQuality>,
2028 pub OperatingBandwidth: Vec<OperatingBandwidth>,
2029 pub ContentSteering: Vec<ContentSteering>,
2030 pub ClientDataReporting: Vec<ClientDataReporting>,
2031 pub PlaybackRestrictions: Vec<PlaybackRestrictions>,
2032 #[serde(rename = "@id")]
2033 pub id: Option<String>,
2034}
2035
2036#[skip_serializing_none]
2038#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2039#[serde(default)]
2040pub struct UTCTiming {
2041 #[serde(rename = "@id")]
2042 pub id: Option<String>,
2043 #[serde(rename = "@schemeIdUri")]
2046 pub schemeIdUri: String,
2047 #[serde(rename = "@value")]
2048 pub value: Option<String>,
2049}
2050
2051#[skip_serializing_none]
2056#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2057#[serde(default)]
2058pub struct ProducerReferenceTime {
2059 #[serde(rename = "@id")]
2061 pub id: Option<String>,
2062 #[serde(rename = "@inband", default = "default_optbool_false")]
2063 pub inband: Option<bool>,
2064 #[serde(rename = "@presentationTime")]
2066 pub presentationTime: Option<u64>,
2067 #[serde(rename = "@type", default = "default_optstring_encoder")]
2068 pub prtType: Option<String>,
2069 #[serde(rename = "@wallClockTime",
2073 alias="@wallclockTime",
2074 deserialize_with = "deserialize_xs_datetime",
2075 default)]
2076 pub wallClockTime: Option<XsDatetime>,
2077 pub UTCTiming: Option<UTCTiming>,
2078}
2079
2080#[skip_serializing_none]
2081#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2082#[serde(default)]
2083pub struct LeapSecondInformation {
2084 #[serde(rename = "@availabilityStartLeapOffset")]
2085 pub availabilityStartLeapOffset: Option<i64>,
2086 #[serde(rename = "@nextAvailabilityStartLeapOffset")]
2087 pub nextAvailabilityStartLeapOffset: Option<i64>,
2088 #[serde(rename = "@nextLeapChangeTime",
2089 deserialize_with = "deserialize_xs_datetime",
2090 default)]
2091 pub nextLeapChangeTime: Option<XsDatetime>,
2092}
2093
2094#[skip_serializing_none]
2101#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2102#[serde(default)]
2103pub struct PatchLocation {
2104 #[serde(rename = "@ttl", serialize_with="serialize_opt_xsd_double")]
2105 pub ttl: Option<f64>,
2106 #[serde(rename = "$text")]
2107 pub content: String,
2108}
2109
2110#[skip_serializing_none]
2112#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2113#[serde(default)]
2114pub struct MPD {
2115 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
2116 pub xmlns: Option<String>,
2117 #[serde(rename = "@id")]
2118 pub id: Option<String>,
2119 #[serde(rename = "@profiles")]
2120 pub profiles: Option<String>,
2121 #[serde(rename = "@type")]
2124 pub mpdtype: Option<String>,
2125 #[serde(rename = "@availabilityStartTime",
2126 deserialize_with = "deserialize_xs_datetime",
2127 default)]
2128 pub availabilityStartTime: Option<XsDatetime>,
2129 #[serde(rename = "@availabilityEndTime",
2130 deserialize_with = "deserialize_xs_datetime",
2131 default)]
2132 pub availabilityEndTime: Option<XsDatetime>,
2133 #[serde(rename = "@publishTime",
2134 deserialize_with = "deserialize_xs_datetime",
2135 default)]
2136 pub publishTime: Option<XsDatetime>,
2137 #[serde(rename = "@mediaPresentationDuration",
2138 serialize_with = "serialize_xs_duration",
2139 deserialize_with = "deserialize_xs_duration",
2140 default)]
2141 pub mediaPresentationDuration: Option<Duration>,
2142 #[serde(rename = "@minimumUpdatePeriod",
2143 serialize_with = "serialize_xs_duration",
2144 deserialize_with = "deserialize_xs_duration",
2145 default)]
2146 pub minimumUpdatePeriod: Option<Duration>,
2147 #[serde(rename = "@minBufferTime",
2149 serialize_with = "serialize_xs_duration",
2150 deserialize_with = "deserialize_xs_duration",
2151 default)]
2152 pub minBufferTime: Option<Duration>,
2153 #[serde(rename = "@timeShiftBufferDepth",
2156 serialize_with = "serialize_xs_duration",
2157 deserialize_with = "deserialize_xs_duration",
2158 default)]
2159 pub timeShiftBufferDepth: Option<Duration>,
2160 #[serde(rename = "@suggestedPresentationDelay",
2162 serialize_with = "serialize_xs_duration",
2163 deserialize_with = "deserialize_xs_duration",
2164 default)]
2165 pub suggestedPresentationDelay: Option<Duration>,
2166 #[serde(rename = "@maxSegmentDuration",
2167 serialize_with = "serialize_xs_duration",
2168 deserialize_with = "deserialize_xs_duration",
2169 default)]
2170 pub maxSegmentDuration: Option<Duration>,
2171 #[serde(rename = "@maxSubsegmentDuration",
2172 serialize_with = "serialize_xs_duration",
2173 deserialize_with = "deserialize_xs_duration",
2174 default)]
2175 pub maxSubsegmentDuration: Option<Duration>,
2176 #[serialize_always]
2178 #[serde(rename="@xmlns:xsi", alias="@xsi", serialize_with="serialize_xsi_ns")]
2179 pub xsi: Option<String>,
2180 #[serde(alias = "@ext", rename = "@xmlns:ext")]
2181 pub ext: Option<String>,
2182 #[serialize_always]
2184 #[serde(rename="@xmlns:cenc", alias="@cenc", serialize_with="serialize_cenc_ns")]
2185 pub cenc: Option<String>,
2186 #[serialize_always]
2188 #[serde(rename="@xmlns:mspr", alias="@mspr", serialize_with="serialize_mspr_ns")]
2189 pub mspr: Option<String>,
2190 #[serialize_always]
2192 #[serde(rename="@xmlns:xlink", alias="@xlink", serialize_with="serialize_xlink_ns")]
2193 pub xlink: Option<String>,
2194 #[cfg(feature = "scte35")]
2197 #[serialize_always]
2198 #[serde(rename="@xmlns:scte35", alias="@scte35", serialize_with="scte35::serialize_scte35_ns")]
2199 pub scte35: Option<String>,
2200 #[serialize_always]
2203 #[serde(rename="@xmlns:dvb", alias="@dvb", serialize_with="serialize_dvb_ns")]
2204 pub dvb: Option<String>,
2205 #[serde(rename = "@xsi:schemaLocation", alias = "@schemaLocation")]
2206 pub schemaLocation: Option<String>,
2207 #[serde(alias = "@scte214", rename = "@xmlns:scte214")]
2209 pub scte214: Option<String>,
2210 pub ProgramInformation: Vec<ProgramInformation>,
2211 #[serde(rename = "BaseURL")]
2213 pub base_url: Vec<BaseURL>,
2214 #[serde(rename = "Location", default)]
2215 pub locations: Vec<Location>,
2216 pub PatchLocation: Vec<PatchLocation>,
2219 pub ServiceDescription: Vec<ServiceDescription>,
2220 pub ContentProtection: Vec<ContentProtection>,
2222 #[serde(rename = "Period", default)]
2223 pub periods: Vec<Period>,
2224 pub Metrics: Vec<Metrics>,
2225 #[serde(rename = "EssentialProperty")]
2226 pub essential_property: Vec<EssentialProperty>,
2227 #[serde(rename = "SupplementalProperty")]
2228 pub supplemental_property: Vec<SupplementalProperty>,
2229 pub UTCTiming: Vec<UTCTiming>,
2230 pub LeapSecondInformation: Option<LeapSecondInformation>,
2232}
2233
2234impl std::fmt::Display for MPD {
2235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2236 write!(f, "{}", quick_xml::se::to_string(self).map_err(|_| std::fmt::Error)?)
2237 }
2238}
2239
2240pub fn parse(xml: &str) -> Result<MPD, DashMpdError> {
2242 #[cfg(feature = "warn_ignored_elements")]
2243 {
2244 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2245 let _: MPD = serde_ignored::deserialize(xd, |path| {
2246 warn!("Unused XML element in manifest: {path}");
2247 }).map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2248 }
2249 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2250 let mpd: MPD = serde_path_to_error::deserialize(xd)
2251 .map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2252 Ok(mpd)
2253}
2254
2255
2256fn is_audio_codec(name: &str) -> bool {
2258 name.starts_with("mp4a") ||
2259 name.starts_with("aac") ||
2260 name.starts_with("vorbis") ||
2261 name.starts_with("opus") ||
2262 name.starts_with("ogg") ||
2263 name.starts_with("webm") ||
2264 name.starts_with("flac") ||
2265 name.starts_with("mp3") ||
2266 name.starts_with("mpeg") ||
2267 name.starts_with("3gpp") ||
2268 name.starts_with("wav") ||
2269 name.starts_with("ec-3") ||
2270 name.starts_with("ac-4") ||
2271 name.starts_with("dtsc") ||
2272 name.starts_with("aptx") ||
2273 name.starts_with("aiff") ||
2274 name.starts_with("mha1") }
2276
2277
2278#[must_use]
2284pub fn is_audio_adaptation(a: &&AdaptationSet) -> bool {
2285 if let Some(codec) = &a.codecs {
2286 if is_audio_codec(codec) {
2287 return true;
2288 }
2289 }
2290 if let Some(ct) = &a.contentType {
2291 if ct == "audio" {
2292 return true;
2293 }
2294 }
2295 if let Some(mimetype) = &a.mimeType {
2296 if mimetype.starts_with("audio/") {
2297 return true;
2298 }
2299 }
2300 for r in &a.representations {
2301 if let Some(ct) = &r.contentType {
2302 if ct == "audio" {
2303 return true;
2304 }
2305 }
2306 if let Some(mimetype) = &r.mimeType {
2307 if mimetype.starts_with("audio/") {
2308 return true;
2309 }
2310 }
2311 }
2312 false
2313}
2314
2315pub fn is_video_adaptation(a: &&AdaptationSet) -> bool {
2324 if is_audio_adaptation(a) {
2325 return false;
2326 }
2327 if let Some(ct) = &a.contentType {
2328 if ct == "video" {
2329 return true;
2330 }
2331 }
2332 if let Some(mimetype) = &a.mimeType {
2333 if mimetype.starts_with("video/") {
2334 return true;
2335 }
2336 }
2337 for r in &a.representations {
2338 if let Some(ct) = &r.contentType {
2339 if ct == "video" {
2340 return true;
2341 }
2342 }
2343 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2346 return false;
2347 }
2348 if let Some(mimetype) = &r.mimeType {
2349 if mimetype.starts_with("video/") {
2350 return true;
2351 }
2352 }
2353 }
2354 false
2355}
2356
2357
2358fn is_subtitle_mimetype(mt: &str) -> bool {
2359 mt.eq("text/vtt") ||
2360 mt.eq("application/ttml+xml") ||
2361 mt.eq("application/x-sami")
2362
2363 }
2366
2367fn is_subtitle_codec(c: &str) -> bool {
2368 c == "wvtt" ||
2369 c == "c608" ||
2370 c == "stpp" ||
2371 c == "tx3g" ||
2372 c.starts_with("stpp.")
2373}
2374
2375pub fn is_subtitle_adaptation(a: &&AdaptationSet) -> bool {
2387 if a.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2388 return true;
2389 }
2390 if a.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2391 return true;
2392 }
2393 if a.codecs.as_deref().is_some_and(is_subtitle_codec) {
2394 return true;
2395 }
2396 for cc in &a.ContentComponent {
2397 if cc.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2398 return true;
2399 }
2400 }
2401 for r in &a.Role {
2402 if r.value.as_deref().is_some_and(|rr| rr.eq("subtitle")) {
2403 return true;
2404 }
2405 }
2406 for r in &a.representations {
2407 if r.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2408 return true;
2409 }
2410 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2412 return true;
2413 }
2414 }
2415 false
2416}
2417
2418
2419#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2421pub enum SubtitleType {
2422 Vtt,
2424 Srt,
2426 Sub,
2428 Ass,
2430 Ttxt,
2432 Ttml,
2434 Sami,
2436 Wvtt,
2440 Stpp,
2442 Eia608,
2444 Unknown,
2445}
2446
2447fn subtitle_type_for_mimetype(mt: &str) -> Option<SubtitleType> {
2448 match mt {
2449 "text/vtt" => Some(SubtitleType::Vtt),
2450 "application/ttml+xml" => Some(SubtitleType::Ttml),
2451 "application/x-sami" => Some(SubtitleType::Sami),
2452 _ => None
2453 }
2454}
2455
2456#[must_use]
2457pub fn subtitle_type(a: &&AdaptationSet) -> SubtitleType {
2458 if let Some(mimetype) = &a.mimeType {
2459 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2460 return st;
2461 }
2462 }
2463 if let Some(codecs) = &a.codecs {
2464 if codecs == "wvtt" {
2465 return SubtitleType::Wvtt;
2467 }
2468 if codecs == "c608" {
2469 return SubtitleType::Eia608;
2470 }
2471 if codecs == "tx3g" {
2472 return SubtitleType::Ttxt;
2473 }
2474 if codecs == "stpp" {
2475 return SubtitleType::Stpp;
2476 }
2477 if codecs.starts_with("stpp.") {
2478 return SubtitleType::Stpp;
2479 }
2480 }
2481 for r in &a.representations {
2482 if let Some(mimetype) = &r.mimeType {
2483 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2484 return st;
2485 }
2486 }
2487 if let Some(codecs) = &r.codecs {
2488 if codecs == "wvtt" {
2489 return SubtitleType::Wvtt;
2490 }
2491 if codecs == "c608" {
2492 return SubtitleType::Eia608;
2493 }
2494 if codecs == "tx3g" {
2495 return SubtitleType::Ttxt;
2496 }
2497 if codecs == "stpp" {
2498 return SubtitleType::Stpp;
2499 }
2500 if codecs.starts_with("stpp.") {
2501 return SubtitleType::Stpp;
2502 }
2503 }
2504 }
2505 SubtitleType::Unknown
2506}
2507
2508
2509#[allow(dead_code)]
2510fn content_protection_type(cp: &ContentProtection) -> String {
2511 if let Some(v) = &cp.value {
2512 if v.eq("cenc") {
2513 return String::from("cenc");
2514 }
2515 if v.eq("Widevine") {
2516 return String::from("Widevine");
2517 }
2518 if v.eq("MSPR 2.0") {
2519 return String::from("PlayReady");
2520 }
2521 }
2522 let uri = &cp.schemeIdUri;
2524 let uri = uri.to_lowercase();
2525 if uri.eq("urn:mpeg:dash:mp4protection:2011") {
2526 return String::from("cenc");
2527 }
2528 if uri.eq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed") {
2529 return String::from("Widevine");
2530 }
2531 if uri.eq("urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95") {
2532 return String::from("PlayReady");
2533 }
2534 if uri.eq("urn:uuid:94ce86fb-07ff-4f43-adb8-93d2fa968ca2") {
2535 return String::from("FairPlay");
2536 }
2537 if uri.eq("urn:uuid:3ea8778f-7742-4bf9-b18b-e834b2acbd47") {
2538 return String::from("Clear Key AES-128");
2539 }
2540 if uri.eq("urn:uuid:be58615b-19c4-4684-88b3-c8c57e99e957") {
2541 return String::from("Clear Key SAMPLE-AES");
2542 }
2543 if uri.eq("urn:uuid:adb41c24-2dbf-4a6d-958b-4457c0d27b95") {
2544 return String::from("Nagra");
2545 }
2546 if uri.eq("urn:uuid:5e629af5-38da-4063-8977-97ffbd9902d4") {
2547 return String::from("Marlin");
2548 }
2549 if uri.eq("urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb") {
2550 return String::from("Adobe PrimeTime");
2551 }
2552 if uri.eq("urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b") {
2553 return String::from("W3C Common PSSH box");
2554 }
2555 if uri.eq("urn:uuid:80a6be7e-1448-4c37-9e70-d5aebe04c8d2") {
2556 return String::from("Irdeto Content Protection");
2557 }
2558 if uri.eq("urn:uuid:3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c") {
2559 return String::from("WisePlay-ChinaDRM");
2560 }
2561 if uri.eq("urn:uuid:616c7469-6361-7374-2d50-726f74656374") {
2562 return String::from("Alticast");
2563 }
2564 if uri.eq("urn:uuid:6dd8b3c3-45f4-4a68-bf3a-64168d01a4a6") {
2565 return String::from("ABV DRM");
2566 }
2567 if uri.eq("urn:mpeg:dash:sea:2012") {
2569 return String::from("SEA");
2570 }
2571 String::from("<unknown>")
2572}
2573
2574
2575fn check_segment_template_duration(
2576 st: &SegmentTemplate,
2577 max_seg_duration: &Duration,
2578 outer_timescale: u64) -> Vec<String>
2579{
2580 let mut errors = Vec::new();
2581 if let Some(timeline) = &st.SegmentTimeline {
2582 for s in &timeline.segments {
2583 let sd = s.d / st.timescale.unwrap_or(outer_timescale);
2584 if sd > max_seg_duration.as_secs() {
2585 errors.push(String::from("SegmentTimeline has segment@d > @maxSegmentDuration"));
2586 }
2587 }
2588 }
2589 errors
2590}
2591
2592fn check_segment_template_conformity(st: &SegmentTemplate) -> Vec<String> {
2593 let mut errors = Vec::new();
2594 if let Some(md) = &st.media {
2595 if !valid_url_p(md) {
2596 errors.push(format!("invalid URL {md}"));
2597 }
2598 if md.contains("$Number$") && md.contains("$Time") {
2599 errors.push(String::from("both $Number$ and $Time$ are used in media template URL"));
2600 }
2601 }
2602 if let Some(init) = &st.initialization {
2603 if !valid_url_p(init) {
2604 errors.push(format!("invalid URL {init}"));
2605 }
2606 if init.contains("$Number") {
2607 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2608 }
2609 if init.contains("$Time") {
2610 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2611 }
2612 }
2613 if st.duration.is_some() && st.SegmentTimeline.is_some() {
2614 errors.push(String::from("both SegmentTemplate.duration and SegmentTemplate.SegmentTimeline present"));
2615 }
2616 errors
2617}
2618
2619
2620fn valid_url_p(u: &str) -> bool {
2623 use url::ParseError;
2624
2625 match Url::parse(u) {
2626 Ok(url) => {
2627 url.scheme() == "https" ||
2628 url.scheme() == "http" ||
2629 url.scheme() == "ftp" ||
2630 url.scheme() == "file" ||
2631 url.scheme() == "data"
2632 },
2633 Err(ParseError::RelativeUrlWithoutBase) => true,
2634 Err(_) => false,
2635 }
2636}
2637
2638#[must_use]
2640pub fn check_conformity(mpd: &MPD) -> Vec<String> {
2641 let mut errors = Vec::new();
2642
2643 for p in &mpd.periods {
2646 if p.adaptations.is_empty() {
2647 errors.push(format!("Period with @id {} contains no AdaptationSet elements",
2648 p.id.clone().unwrap_or(String::from("<unspecified>"))));
2649 }
2650 for a in &p.adaptations {
2651 if let Some(mh) = a.maxHeight {
2652 if let Some(mr) = a.representations.iter().max_by_key(|r| r.height.unwrap_or(0)) {
2653 if mr.height.unwrap_or(0) > mh {
2654 errors.push(String::from("invalid @maxHeight on AdaptationSet"));
2655 }
2656 }
2657 }
2658 }
2659 }
2660 for p in &mpd.periods {
2663 for a in &p.adaptations {
2664 if let Some(mw) = a.maxWidth {
2665 if let Some(mr) = a.representations.iter().max_by_key(|r| r.width.unwrap_or(0)) {
2666 if mr.width.unwrap_or(0) > mw {
2667 errors.push(String::from("invalid @maxWidth on AdaptationSet"));
2668 }
2669 }
2670 }
2671 }
2672 }
2673 for p in &mpd.periods {
2676 for a in &p.adaptations {
2677 if let Some(mb) = a.maxBandwidth {
2678 if let Some(mr) = a.representations.iter().max_by_key(|r| r.bandwidth.unwrap_or(0)) {
2679 if mr.bandwidth.unwrap_or(0) > mb {
2680 errors.push(String::from("invalid @maxBandwidth on AdaptationSet"));
2681 }
2682 }
2683 }
2684 }
2685 }
2686 if let Some(max_seg_duration) = mpd.maxSegmentDuration {
2688 for p in &mpd.periods {
2689 for a in &p.adaptations {
2690 let mut outer_timescale = 1;
2701 if let Some(st) = &a.SegmentTemplate {
2702 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2703 .into_iter()
2704 .for_each(|msg| errors.push(msg));
2705 if let Some(ots) = st.timescale {
2706 outer_timescale = ots;
2707 }
2708 }
2709 for r in &a.representations {
2710 if let Some(st) = &r.SegmentTemplate {
2711 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2712 .into_iter()
2713 .for_each(|msg| errors.push(msg));
2714 }
2715 }
2716 }
2717 }
2718 }
2719
2720 for bu in &mpd.base_url {
2721 if !valid_url_p(&bu.base) {
2722 errors.push(format!("invalid URL {}", bu.base));
2723 }
2724 }
2725 for p in &mpd.periods {
2726 for bu in &p.BaseURL {
2727 if !valid_url_p(&bu.base) {
2728 errors.push(format!("invalid URL {}", bu.base));
2729 }
2730 }
2731 for a in &p.adaptations {
2732 for bu in &a.BaseURL {
2733 if !valid_url_p(&bu.base) {
2734 errors.push(format!("invalid URL {}", bu.base));
2735 }
2736 }
2737 if let Some(st) = &a.SegmentTemplate {
2738 check_segment_template_conformity(st)
2739 .into_iter()
2740 .for_each(|msg| errors.push(msg));
2741 }
2742 for r in &a.representations {
2743 for bu in &r.BaseURL {
2744 if !valid_url_p(&bu.base) {
2745 errors.push(format!("invalid URL {}", bu.base));
2746 }
2747 }
2748 if let Some(sb) = &r.SegmentBase {
2749 if let Some(init) = &sb.Initialization {
2750 if let Some(su) = &init.sourceURL {
2751 if !valid_url_p(su) {
2752 errors.push(format!("invalid URL {su}"));
2753 }
2754 if su.contains("$Number") {
2755 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2756 }
2757 if su.contains("$Time") {
2758 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2759 }
2760 }
2761 }
2762 if let Some(ri) = &sb.representation_index {
2763 if let Some(su) = &ri.sourceURL {
2764 if !valid_url_p(su) {
2765 errors.push(format!("invalid URL {su}"));
2766 }
2767 }
2768 }
2769 }
2770 if let Some(sl) = &r.SegmentList {
2771 if let Some(hr) = &sl.href {
2772 if !valid_url_p(hr) {
2773 errors.push(format!("invalid URL {hr}"));
2774 }
2775 }
2776 if let Some(init) = &sl.Initialization {
2777 if let Some(su) = &init.sourceURL {
2778 if !valid_url_p(su) {
2779 errors.push(format!("invalid URL {su}"));
2780 }
2781 if su.contains("$Number") {
2782 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2783 }
2784 if su.contains("$Time") {
2785 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2786 }
2787 }
2788 }
2789 for su in &sl.segment_urls {
2790 if let Some(md) = &su.media {
2791 if !valid_url_p(md) {
2792 errors.push(format!("invalid URL {md}"));
2793 }
2794 }
2795 if let Some(ix) = &su.index {
2796 if !valid_url_p(ix) {
2797 errors.push(format!("invalid URL {ix}"));
2798 }
2799 }
2800 }
2801 }
2802 if let Some(st) = &r.SegmentTemplate {
2803 check_segment_template_conformity(st)
2804 .into_iter()
2805 .for_each(|msg| errors.push(msg));
2806 }
2807 }
2808 }
2809 }
2810 for pi in &mpd.ProgramInformation {
2811 if let Some(u) = &pi.moreInformationURL {
2812 if !valid_url_p(u) {
2813 errors.push(format!("invalid URL {u}"));
2814 }
2815 }
2816 }
2817 errors
2818}
2819
2820#[cfg(test)]
2821mod tests {
2822 use proptest::prelude::*;
2823 use std::fs;
2824 use std::path::PathBuf;
2825 use std::time::Duration;
2826
2827 proptest! {
2828 #[test]
2829 fn doesnt_crash(s in "\\PC*") {
2830 let _ = super::parse_xs_duration(&s);
2831 let _ = super::parse_xs_datetime(&s);
2832 }
2833 }
2834
2835 #[test]
2836 fn test_parse_xs_duration() {
2837 use super::parse_xs_duration;
2838
2839 assert!(parse_xs_duration("").is_err());
2840 assert!(parse_xs_duration("foobles").is_err());
2841 assert!(parse_xs_duration("P").is_err());
2842 assert!(parse_xs_duration("PW").is_err());
2843 assert!(parse_xs_duration("-PT4.5S").is_err());
2845 assert!(parse_xs_duration("1Y2M3DT4H5M6S").is_err()); assert_eq!(parse_xs_duration("PT3H11M53S").ok(), Some(Duration::new(11513, 0)));
2847 assert_eq!(parse_xs_duration("PT42M30S").ok(), Some(Duration::new(2550, 0)));
2848 assert_eq!(parse_xs_duration("PT30M38S").ok(), Some(Duration::new(1838, 0)));
2849 assert_eq!(parse_xs_duration("PT0H10M0.00S").ok(), Some(Duration::new(600, 0)));
2850 assert_eq!(parse_xs_duration("PT1.5S").ok(), Some(Duration::new(1, 500_000_000)));
2851 assert_eq!(parse_xs_duration("PT1.500S").ok(), Some(Duration::new(1, 500_000_000)));
2852 assert_eq!(parse_xs_duration("PT1.500000000S").ok(), Some(Duration::new(1, 500_000_000)));
2853 assert_eq!(parse_xs_duration("PT0S").ok(), Some(Duration::new(0, 0)));
2854 assert_eq!(parse_xs_duration("PT0.001S").ok(), Some(Duration::new(0, 1_000_000)));
2855 assert_eq!(parse_xs_duration("PT0.00100S").ok(), Some(Duration::new(0, 1_000_000)));
2856 assert_eq!(parse_xs_duration("PT344S").ok(), Some(Duration::new(344, 0)));
2857 assert_eq!(parse_xs_duration("PT634.566S").ok(), Some(Duration::new(634, 566_000_000)));
2858 assert_eq!(parse_xs_duration("PT72H").ok(), Some(Duration::new(72*60*60, 0)));
2859 assert_eq!(parse_xs_duration("PT0H0M30.030S").ok(), Some(Duration::new(30, 30_000_000)));
2860 assert_eq!(parse_xs_duration("PT1004199059S").ok(), Some(Duration::new(1004199059, 0)));
2861 assert_eq!(parse_xs_duration("P0Y20M0D").ok(), Some(Duration::new(51840000, 0)));
2862 assert_eq!(parse_xs_duration("PT1M30.5S").ok(), Some(Duration::new(90, 500_000_000)));
2863 assert_eq!(parse_xs_duration("PT10M10S").ok(), Some(Duration::new(610, 0)));
2864 assert_eq!(parse_xs_duration("PT1H0.040S").ok(), Some(Duration::new(3600, 40_000_000)));
2865 assert_eq!(parse_xs_duration("PT00H03M30SZ").ok(), Some(Duration::new(210, 0)));
2866 assert_eq!(parse_xs_duration("PT3.14159S").ok(), Some(Duration::new(3, 141_590_000)));
2867 assert_eq!(parse_xs_duration("PT3.14159265S").ok(), Some(Duration::new(3, 141_592_650)));
2868 assert_eq!(parse_xs_duration("PT3.141592653S").ok(), Some(Duration::new(3, 141_592_653)));
2869 assert_eq!(parse_xs_duration("PT3.141592653897S").ok(), Some(Duration::new(3, 141_592_653)));
2871 assert_eq!(parse_xs_duration("P0W").ok(), Some(Duration::new(0, 0)));
2872 assert_eq!(parse_xs_duration("P26W").ok(), Some(Duration::new(15724800, 0)));
2873 assert_eq!(parse_xs_duration("P52W").ok(), Some(Duration::new(31449600, 0)));
2874 assert_eq!(parse_xs_duration("P10D").ok(), Some(Duration::new(864000, 0)));
2875 assert_eq!(parse_xs_duration("P0Y").ok(), Some(Duration::new(0, 0)));
2876 assert_eq!(parse_xs_duration("P1Y").ok(), Some(Duration::new(31536000, 0)));
2877 assert_eq!(parse_xs_duration("P1Y0W0S").ok(), Some(Duration::new(31536000, 0)));
2878 assert_eq!(parse_xs_duration("PT4H").ok(), Some(Duration::new(14400, 0)));
2879 assert_eq!(parse_xs_duration("+PT4H").ok(), Some(Duration::new(14400, 0)));
2880 assert_eq!(parse_xs_duration("PT0004H").ok(), Some(Duration::new(14400, 0)));
2881 assert_eq!(parse_xs_duration("PT4H0M").ok(), Some(Duration::new(14400, 0)));
2882 assert_eq!(parse_xs_duration("PT4H0S").ok(), Some(Duration::new(14400, 0)));
2883 assert_eq!(parse_xs_duration("P23DT23H").ok(), Some(Duration::new(2070000, 0)));
2884 assert_eq!(parse_xs_duration("P0Y0M0DT0H4M20.880S").ok(), Some(Duration::new(260, 880_000_000)));
2885 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6.7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2886 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6,7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2887
2888 }
2892
2893 #[test]
2894 fn test_serialize_xs_duration() {
2895 use super::MPD;
2896
2897 fn serialized_xs_duration(d: Duration) -> String {
2898 let mpd = MPD {
2899 minBufferTime: Some(d),
2900 ..Default::default()
2901 };
2902 let xml = mpd.to_string();
2903 let doc = roxmltree::Document::parse(&xml).unwrap();
2904 String::from(doc.root_element().attribute("minBufferTime").unwrap())
2905 }
2906
2907 assert_eq!("PT0S", serialized_xs_duration(Duration::new(0, 0)));
2908 assert_eq!("PT0.001S", serialized_xs_duration(Duration::new(0, 1_000_000)));
2909 assert_eq!("PT42S", serialized_xs_duration(Duration::new(42, 0)));
2910 assert_eq!("PT1.5S", serialized_xs_duration(Duration::new(1, 500_000_000)));
2911 assert_eq!("PT30.03S", serialized_xs_duration(Duration::new(30, 30_000_000)));
2912 assert_eq!("PT1M30.5S", serialized_xs_duration(Duration::new(90, 500_000_000)));
2913 assert_eq!("PT5M44S", serialized_xs_duration(Duration::new(344, 0)));
2914 assert_eq!("PT42M30S", serialized_xs_duration(Duration::new(2550, 0)));
2915 assert_eq!("PT30M38S", serialized_xs_duration(Duration::new(1838, 0)));
2916 assert_eq!("PT10M10S", serialized_xs_duration(Duration::new(610, 0)));
2917 assert_eq!("PT1H0M0.04S", serialized_xs_duration(Duration::new(3600, 40_000_000)));
2918 assert_eq!("PT3H11M53S", serialized_xs_duration(Duration::new(11513, 0)));
2919 assert_eq!("PT4H", serialized_xs_duration(Duration::new(14400, 0)));
2920 }
2921
2922 #[test]
2923 fn test_parse_xs_datetime() {
2924 use chrono::{DateTime, NaiveDate};
2925 use chrono::offset::Utc;
2926 use super::parse_xs_datetime;
2927
2928 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2929 .unwrap()
2930 .and_hms_opt(1, 3, 2)
2931 .unwrap();
2932 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02Z").ok(),
2933 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2934 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2935 .unwrap()
2936 .and_hms_nano_opt(1, 3, 2, 958*1000*1000)
2937 .unwrap();
2938 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02.958Z").ok(),
2939 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2940 }
2941
2942 #[test]
2943 fn test_parse_failure() {
2944 use super::parse;
2945
2946 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2947 path.push("tests");
2948 path.push("fixtures");
2949 path.push("incomplete.mpd");
2950 let xml = fs::read_to_string(path).unwrap();
2951 assert!(matches!(parse(&xml), Err(crate::DashMpdError::Parsing(_))));
2952 }
2953
2954 #[test]
2955 fn test_conformity_checking() {
2956 use super::{parse, check_conformity};
2957
2958 for fixture in [
2960 "a2d-tv.mpd",
2961 "ad-insertion-testcase1.mpd",
2962 "ad-insertion-testcase6-av1.mpd",
2963 "ad-insertion-testcase6-av2.mpd",
2964 "ad-insertion-testcase6-av5.mpd",
2965 "aws.xml",
2966 "dashif-live-atoinf.mpd",
2967 "dashif-low-latency.mpd",
2968 "dash-testcases-5b-1-thomson.mpd",
2969 "dolby-ac4.xml",
2970 "example_G22.mpd",
2971 "f64-inf.mpd",
2972 "jurassic-compact-5975.mpd",
2973 "mediapackage.xml",
2974 "multiple_supplementals.mpd",
2975 "orange.xml",
2976 "patch-location.mpd",
2977 "st-sl.mpd",
2978 "telenet-mid-ad-rolls.mpd",
2979 "manifest_wvcenc_1080p.mpd"] {
2980 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2981 path.push("tests");
2982 path.push("fixtures");
2983 path.push(fixture);
2984 let xml = fs::read_to_string(path)
2985 .unwrap_or_else(|_| panic!("failed to read fixture {fixture}"));
2986 let mpd = parse(&xml)
2987 .unwrap_or_else(|_| panic!("failed to parse fixture {fixture}"));
2988 let anomalies = check_conformity(&mpd);
2989 assert!(anomalies.is_empty());
2990 }
2991 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2993 path.push("tests");
2994 path.push("fixtures");
2995 path.push("admanager.xml");
2996 let xml = fs::read_to_string(path).unwrap();
2997 let mpd = parse(&xml).unwrap();
2998 let anomalies = check_conformity(&mpd);
2999 assert!(!anomalies.is_empty());
3000 for anomaly in anomalies {
3001 assert!(anomaly.starts_with("SegmentTimeline has segment@d"));
3002 }
3003 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3004 path.push("tests");
3005 path.push("fixtures");
3006 path.push( "avod-mediatailor.mpd");
3007 let xml = fs::read_to_string(path).unwrap();
3008 let mpd = parse(&xml).unwrap();
3009 let anomalies = check_conformity(&mpd);
3010 assert!(!anomalies.is_empty());
3011 for anomaly in anomalies {
3012 assert!(anomaly.starts_with("SegmentTimeline has segment@d"));
3013 }
3014 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3015 path.push("tests");
3016 path.push("fixtures");
3017 path.push("telestream-binary.xml");
3018 let xml = fs::read_to_string(path).unwrap();
3019 let mpd = parse(&xml).unwrap();
3020 let anomalies = check_conformity(&mpd);
3021 assert!(!anomalies.is_empty());
3022 for anomaly in anomalies {
3023 assert!(anomaly.starts_with("Period with @id <unspecified> contains no AdaptationSet elements"));
3024 }
3025 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3026 path.push("tests");
3027 path.push("fixtures");
3028 path.push("telestream-elements.xml");
3029 let xml = fs::read_to_string(path).unwrap();
3030 let mpd = parse(&xml).unwrap();
3031 let anomalies = check_conformity(&mpd);
3032 assert!(!anomalies.is_empty());
3033 for anomaly in anomalies {
3034 assert!(anomaly.starts_with("Period with @id <unspecified> contains no AdaptationSet elements"));
3035 }
3036 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3037 path.push("tests");
3038 path.push("fixtures");
3039 path.push("vod-aip-unif-streaming.mpd");
3040 let xml = fs::read_to_string(path).unwrap();
3041 let mpd = parse(&xml).unwrap();
3042 let anomalies = check_conformity(&mpd);
3043 assert!(!anomalies.is_empty());
3044 for anomaly in anomalies {
3045 assert!(anomaly.starts_with("SegmentTimeline has segment@d > @maxSegmentDuration"));
3046 }
3047 }
3048}