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