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