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};
90use serde::{Serialize, Serializer, Deserialize};
91use serde::de;
92use serde_with::skip_serializing_none;
93use regex::Regex;
94use std::sync::LazyLock;
95use std::time::Duration;
96use chrono::DateTime;
97use url::Url;
98#[allow(unused_imports)]
99use tracing::warn;
100
101static XS_DURATION_REGEX: LazyLock<Regex> = LazyLock::new(||
103 Regex::new(concat!(r"^(?P<sign>[+-])?P",
104 r"(?:(?P<years>\d+)Y)?",
105 r"(?:(?P<months>\d+)M)?",
106 r"(?:(?P<weeks>\d+)W)?",
107 r"(?:(?P<days>\d+)D)?",
108 r"(?:(?P<hastime>T)", r"(?:(?P<hours>\d+)H)?",
110 r"(?:(?P<minutes>\d+)M)?",
111 r"(?:(?P<seconds>\d+)(?:(?P<nanoseconds>[.,]\d+)?)S)?",
112 r")?")).unwrap()
113);
114
115pub type XsDatetime = DateTime<chrono::offset::Utc>;
118
119#[derive(thiserror::Error, Debug)]
120#[non_exhaustive]
121pub enum DashMpdError {
122 #[error("parse error {0:?}")]
123 Parsing(String),
124 #[error("invalid Duration: {0:?}")]
125 InvalidDuration(String),
126 #[error("invalid DateTime: {0:?}")]
127 InvalidDateTime(String),
128 #[error("invalid media stream: {0:?}")]
129 UnhandledMediaStream(String),
130 #[error("I/O error {1} ({0:?})")]
131 Io(#[source] std::io::Error, String),
132 #[error("network error {0:?}")]
133 Network(String),
134 #[error("network timeout: {0:?}")]
135 NetworkTimeout(String),
136 #[error("network connection: {0:?}")]
137 NetworkConnect(String),
138 #[error("muxing error {0:?}")]
139 Muxing(String),
140 #[error("decryption error {0:?}")]
141 Decrypting(String),
142 #[error("{0:?}")]
143 Other(String),
144}
145
146
147fn serialize_xsd_double<S>(xsd: &f64, serializer: S) -> Result<S::Ok, S::Error>
152where
153 S: Serializer,
154{
155 let formatted = if xsd.is_nan() {
156 String::from("NaN")
157 } else if xsd.is_infinite() {
158 if xsd.is_sign_positive() {
159 String::from("INF")
161 } else {
162 String::from("-INF")
163 }
164 } else {
165 xsd.to_string()
166 };
167 serializer.serialize_str(&formatted)
168}
169
170fn serialize_opt_xsd_double<S>(oxsd: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
172where
173 S: Serializer,
174{
175 if let Some(xsd) = oxsd {
176 serialize_xsd_double(xsd, serializer)
177 } else {
178 serializer.serialize_none()
180 }
181}
182
183
184fn parse_xs_duration(s: &str) -> Result<Duration, DashMpdError> {
200 use std::cmp::min;
201
202 match XS_DURATION_REGEX.captures(s) {
203 Some(m) => {
204 if m.name("hastime").is_none() &&
205 m.name("years").is_none() &&
206 m.name("months").is_none() &&
207 m.name("weeks").is_none() &&
208 m.name("days").is_none() {
209 return Err(DashMpdError::InvalidDuration("empty".to_string()));
210 }
211 let mut secs: u64 = 0;
212 let mut nsecs: u32 = 0;
213 if let Some(nano) = m.name("nanoseconds") {
214 let lim = min(nano.as_str().len(), 9 + ".".len());
217 if let Some(ss) = &nano.as_str().get(1..lim) {
218 let padded = format!("{ss:0<9}");
219 nsecs = padded.parse::<u32>()
220 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
221 }
222 }
223 if let Some(mseconds) = m.name("seconds") {
224 let seconds = mseconds.as_str().parse::<u64>()
225 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
226 secs += seconds;
227 }
228 if let Some(mminutes) = m.name("minutes") {
229 let minutes = mminutes.as_str().parse::<u64>()
230 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
231 secs += minutes * 60;
232 }
233 if let Some(mhours) = m.name("hours") {
234 let hours = mhours.as_str().parse::<u64>()
235 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
236 secs += hours * 60 * 60;
237 }
238 if let Some(mdays) = m.name("days") {
239 let days = mdays.as_str().parse::<u64>()
240 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
241 secs += days * 60 * 60 * 24;
242 }
243 if let Some(mweeks) = m.name("weeks") {
244 let weeks = mweeks.as_str().parse::<u64>()
245 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
246 secs += weeks * 60 * 60 * 24 * 7;
247 }
248 if let Some(mmonths) = m.name("months") {
249 let months = mmonths.as_str().parse::<u64>()
250 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
251 secs += months * 60 * 60 * 24 * 30;
252 }
253 if let Some(myears) = m.name("years") {
254 let years = myears.as_str().parse::<u64>()
255 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
256 secs += years * 60 * 60 * 24 * 365;
257 }
258 if let Some(msign) = m.name("sign") {
259 if msign.as_str() == "-" {
260 return Err(DashMpdError::InvalidDuration("can't represent negative durations".to_string()));
261 }
262 }
263 Ok(Duration::new(secs, nsecs))
264 },
265 None => Err(DashMpdError::InvalidDuration(String::from("couldn't parse XS duration"))),
266 }
267}
268
269
270fn deserialize_xs_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
318where
319 D: de::Deserializer<'de>,
320{
321 match <Option<String>>::deserialize(deserializer) {
322 Ok(optstring) => match optstring {
323 Some(xs) => match parse_xs_duration(&xs) {
324 Ok(d) => Ok(Some(d)),
325 Err(e) => Err(de::Error::custom(e)),
326 },
327 None => Ok(None),
328 },
329 Err(_) => Ok(None),
331 }
332}
333
334fn serialize_xs_duration<S>(oxs: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
342where
343 S: Serializer,
344{
345 if let Some(xs) = oxs {
346 let seconds = xs.as_secs();
347 let nanos = xs.subsec_nanos();
348 let minutes = seconds / 60;
349 let hours = if minutes > 59 { minutes / 60 } else { 0 };
350 let fractional_maybe = if nanos > 0 {
351 format!(".{nanos:09}").trim_end_matches('0').to_string()
352 } else {
353 "".to_string()
354 };
355 let formatted_duration = if hours > 0 {
356 let mins = minutes % 60;
357 let secs = seconds % 60;
358 format!("PT{hours}H{mins}M{secs}{fractional_maybe}S")
359 } else if minutes > 0 {
360 let secs = seconds % 60;
361 format!("PT{minutes}M{secs}{fractional_maybe}S")
362 } else {
363 format!("PT{seconds}{fractional_maybe}S")
364 };
365 serializer.serialize_str(&formatted_duration)
366 } else {
367 serializer.serialize_none()
369 }
370}
371
372
373fn parse_xs_datetime(s: &str) -> Result<XsDatetime, DashMpdError> {
379 use iso8601::Date;
380 use chrono::{LocalResult, NaiveDate, TimeZone};
381 use num_traits::cast::FromPrimitive;
382 match DateTime::<chrono::offset::FixedOffset>::parse_from_rfc3339(s) {
383 Ok(dt) => Ok(dt.into()),
384 Err(_) => match iso8601::datetime(s) {
385 Ok(dt) => {
386 let nd = match dt.date {
387 Date::YMD { year, month, day } =>
388 NaiveDate::from_ymd_opt(year, month, day)
389 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
390 Date::Week { year, ww, d } => {
391 let d = chrono::Weekday::from_u32(d)
392 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
393 NaiveDate::from_isoywd_opt(year, ww, d)
394 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
395 },
396 Date::Ordinal { year, ddd } =>
397 NaiveDate::from_yo_opt(year, ddd)
398 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
399 };
400 let nd = nd.and_hms_nano_opt(dt.time.hour, dt.time.minute, dt.time.second, dt.time.millisecond*1000*1000)
401 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
402 let tz_secs = dt.time.tz_offset_hours * 3600 + dt.time.tz_offset_minutes * 60;
403 match chrono::FixedOffset::east_opt(tz_secs)
404 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
405 .from_local_datetime(&nd)
406 {
407 LocalResult::Single(local) => Ok(local.with_timezone(&chrono::Utc)),
408 _ => Err(DashMpdError::InvalidDateTime(s.to_string())),
409 }
410 },
411 Err(_) => Err(DashMpdError::InvalidDateTime(s.to_string())),
412 }
413 }
414}
415
416fn deserialize_xs_datetime<'de, D>(deserializer: D) -> Result<Option<XsDatetime>, D::Error>
418where
419 D: de::Deserializer<'de>,
420{
421 match <Option<String>>::deserialize(deserializer) {
422 Ok(optstring) => match optstring {
423 Some(xs) => match parse_xs_datetime(&xs) {
424 Ok(d) => Ok(Some(d)),
425 Err(e) => Err(de::Error::custom(e)),
426 },
427 None => Ok(None),
428 },
429 Err(_) => Ok(None),
431 }
432}
433
434fn serialize_xsd_uintvector<S>(v: &Vec<u64>, serializer: S) -> Result<S::Ok, S::Error>
437where
438 S: Serializer,
439{
440 let mut formatted = String::new();
441 for u in v {
442 formatted += &format!("{u} ");
443 }
444 serializer.serialize_str(&formatted)
445}
446
447fn deserialize_xsd_uintvector<'de, D>(deserializer: D) -> Result<Vec<u64>, D::Error>
448where
449 D: de::Deserializer<'de>,
450{
451 let s = String::deserialize(deserializer)?;
452 let mut out = Vec::<u64>::new();
453 for uint64_str in s.split_whitespace() {
454 match uint64_str.parse::<u64>() {
455 Ok(val) => out.push(val),
456 Err(e) => return Err(de::Error::custom(e)),
457 }
458 }
459 Ok(out)
460}
461
462fn serialize_xmlns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
472where S: serde::Serializer {
473 if let Some(s) = os {
474 serializer.serialize_str(s)
475 } else {
476 serializer.serialize_str("urn:mpeg:dash:schema:mpd:2011")
477 }
478}
479
480fn serialize_xsi_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
481where S: serde::Serializer {
482 if let Some(s) = os {
483 serializer.serialize_str(s)
484 } else {
485 serializer.serialize_str("http://www.w3.org/2001/XMLSchema-instance")
486 }
487}
488
489fn serialize_cenc_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
490where S: serde::Serializer {
491 if let Some(s) = os {
492 serializer.serialize_str(s)
493 } else {
494 serializer.serialize_str("urn:mpeg:cenc:2013")
495 }
496}
497
498fn serialize_mspr_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
499where S: serde::Serializer {
500 if let Some(s) = os {
501 serializer.serialize_str(s)
502 } else {
503 serializer.serialize_str("urn:microsoft:playready")
504 }
505}
506
507fn serialize_xlink_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
508where S: serde::Serializer {
509 if let Some(s) = os {
510 serializer.serialize_str(s)
511 } else {
512 serializer.serialize_str("http://www.w3.org/1999/xlink")
513 }
514}
515
516fn serialize_dvb_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
517where S: serde::Serializer {
518 if let Some(s) = os {
519 serializer.serialize_str(s)
520 } else {
521 serializer.serialize_str("urn:dvb:dash-extensions:2014-1")
522 }
523}
524
525
526fn default_optstring_on_request() -> Option<String> {
530 Some("onRequest".to_string())
531}
532
533fn default_optstring_one() -> Option<String> {
534 Some(String::from("1"))
535}
536
537fn default_optstring_encoder() -> Option<String> {
538 Some(String::from("encoder"))
539}
540
541fn default_optstring_any() -> Option<String> {
542 Some(String::from("any"))
543}
544
545fn default_optstring_query() -> Option<String> {
546 Some(String::from("query"))
547}
548
549fn default_optstring_segment() -> Option<String> {
550 Some(String::from("segment"))
551}
552
553fn default_optbool_true() -> Option<bool> {
554 Some(true)
555}
556
557fn default_optbool_false() -> Option<bool> {
558 Some(false)
559}
560
561fn default_optu64_zero() -> Option<u64> {
562 Some(0)
563}
564
565fn default_optu64_one() -> Option<u64> {
566 Some(1)
567}
568
569
570#[skip_serializing_none]
583#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
584#[serde(default)]
585pub struct Title {
586 #[serde(rename = "$text")]
587 pub content: Option<String>,
588}
589
590#[skip_serializing_none]
592#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
593#[serde(default)]
594pub struct Source {
595 #[serde(rename = "$text")]
596 pub content: Option<String>,
597}
598
599#[skip_serializing_none]
601#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
602#[serde(default)]
603pub struct Copyright {
604 #[serde(rename = "$text")]
605 pub content: Option<String>,
606}
607
608#[skip_serializing_none]
610#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
611#[serde(default)]
612pub struct ProgramInformation {
613 #[serde(rename = "@lang")]
615 pub lang: Option<String>,
616 #[serde(rename = "@moreInformationURL")]
617 pub moreInformationURL: Option<String>,
618 pub Title: Option<Title>,
619 pub Source: Option<Source>,
620 pub Copyright: Option<Copyright>,
621 #[serde(rename(serialize = "scte214:ContentIdentifier", deserialize = "ContentIdentifier"))]
622 pub scte214ContentIdentifier: Option<Scte214ContentIdentifier>,
623}
624
625#[skip_serializing_none]
629#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
630#[serde(default)]
631pub struct Scte214ContentIdentifier {
632 #[serde(rename = "@type")]
633 pub idType: Option<String>,
634 #[serde(rename = "@value")]
635 pub idValue: Option<String>,
636}
637
638#[skip_serializing_none]
640#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
641#[serde(default)]
642pub struct S {
643 #[serde(rename = "@t")]
645 pub t: Option<u64>,
646 #[serde(rename = "@n")]
647 pub n: Option<u64>,
648 #[serde(rename = "@d")]
650 pub d: u64,
651 #[serde(rename = "@r")]
654 pub r: Option<i64>,
655 #[serde(rename = "@k")]
656 pub k: Option<u64>,
657}
658
659#[skip_serializing_none]
662#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
663#[serde(default)]
664pub struct SegmentTimeline {
665 #[serde(rename = "S")]
667 pub segments: Vec<S>,
668}
669
670#[skip_serializing_none]
677#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
678#[serde(default)]
679pub struct BitstreamSwitching {
680 #[serde(rename = "@sourceURL")]
681 pub source_url: Option<String>,
682 #[serde(rename = "@range")]
683 pub range: Option<String>,
684}
685
686#[skip_serializing_none]
690#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
691#[serde(default)]
692pub struct Initialization {
693 #[serde(rename = "@sourceURL")]
694 pub sourceURL: Option<String>,
695 #[serde(rename = "@range")]
696 pub range: Option<String>,
697}
698
699#[skip_serializing_none]
700#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
701#[serde(default)]
702pub struct RepresentationIndex {
703 #[serde(rename = "@range")]
704 pub range: Option<String>,
705 #[serde(rename = "@sourceURL")]
706 pub sourceURL: Option<String>,
707}
708
709#[skip_serializing_none]
712#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
713#[serde(default)]
714pub struct SegmentTemplate {
715 #[serde(rename = "@media")]
716 pub media: Option<String>,
717 #[serde(rename = "@index")]
718 pub index: Option<String>,
719 #[serde(rename = "@initialization")]
720 pub initialization: Option<String>,
721 #[serde(rename = "@bitstreamSwitching")]
722 pub bitstreamSwitching: Option<String>,
723 #[serde(rename = "@indexRange")]
724 pub indexRange: Option<String>,
725 #[serde(rename = "@indexRangeExact")]
726 pub indexRangeExact: Option<bool>,
727 #[serde(rename = "@startNumber")]
728 pub startNumber: Option<u64>,
729 #[serde(rename = "@duration")]
733 pub duration: Option<f64>,
734 #[serde(rename = "@timescale")]
735 pub timescale: Option<u64>,
736 #[serde(rename = "@eptDelta")]
738 pub eptDelta: Option<i64>,
739 #[serde(rename = "@pdDelta")]
742 pub pbDelta: Option<i64>,
743 #[serde(rename = "@presentationTimeOffset")]
744 pub presentationTimeOffset: Option<u64>,
745 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
746 pub availabilityTimeOffset: Option<f64>,
747 #[serde(rename = "@availabilityTimeComplete")]
748 pub availabilityTimeComplete: Option<bool>,
749 pub Initialization: Option<Initialization>,
750 #[serde(rename = "RepresentationIndex")]
751 pub representation_index: Option<RepresentationIndex>,
752 #[serde(rename = "FailoverContent")]
756 pub failover_content: Option<FailoverContent>,
757 pub SegmentTimeline: Option<SegmentTimeline>,
758 pub BitstreamSwitching: Option<BitstreamSwitching>,
759}
760
761#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
765#[serde(default)]
766pub struct Location {
767 #[serde(rename = "$text")]
768 pub url: String,
769}
770
771#[skip_serializing_none]
777#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
778#[serde(default)]
779pub struct BaseURL {
780 #[serde(rename = "@serviceLocation")]
781 pub serviceLocation: Option<String>,
782 #[serde(rename = "@byteRange")]
783 pub byte_range: Option<String>,
784 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
787 pub availability_time_offset: Option<f64>,
788 #[serde(rename = "@availabilityTimeComplete")]
789 pub availability_time_complete: Option<bool>,
790 #[serde(rename = "@timeShiftBufferDepth",
791 serialize_with = "serialize_xs_duration",
792 deserialize_with = "deserialize_xs_duration",
793 default)]
794 pub timeShiftBufferDepth: Option<Duration>,
795 #[serde(rename = "@dvb:priority", alias = "@priority")]
797 pub priority: Option<u64>,
798 #[serde(rename = "@dvb:weight", alias = "@weight")]
802 pub weight: Option<i64>,
803 #[serde(rename = "$text")]
804 pub base: String,
805}
806
807#[skip_serializing_none]
813#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
814#[serde(default)]
815pub struct Fcs {
816 #[serde(rename = "@t")]
819 pub t: u64,
820
821 #[serde(rename = "@d")]
824 pub d: Option<u64>,
825}
826
827#[skip_serializing_none]
830#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
831#[serde(default)]
832pub struct FailoverContent {
833 #[serde(rename = "@valid")]
836 pub valid: Option<bool>,
837 #[serde(rename = "FCS")]
838 pub fcs_list: Vec<Fcs>,
839}
840
841#[skip_serializing_none]
843#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
844#[serde(default)]
845pub struct SegmentBase {
846 #[serde(rename = "@timescale")]
847 pub timescale: Option<u64>,
848 #[serde(rename = "@presentationTimeOffset")]
849 pub presentationTimeOffset: Option<u64>,
850 #[serde(rename = "@indexRange")]
851 pub indexRange: Option<String>,
852 #[serde(rename = "@indexRangeExact")]
853 pub indexRangeExact: Option<bool>,
854 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
855 pub availabilityTimeOffset: Option<f64>,
856 #[serde(rename = "@availabilityTimeComplete")]
857 pub availabilityTimeComplete: Option<bool>,
858 #[serde(rename = "@presentationDuration")]
859 pub presentationDuration: Option<u64>,
860 #[serde(rename = "@eptDelta")]
862 pub eptDelta: Option<i64>,
863 #[serde(rename = "@pdDelta")]
866 pub pbDelta: Option<i64>,
867 pub Initialization: Option<Initialization>,
868 #[serde(rename = "RepresentationIndex")]
869 pub representation_index: Option<RepresentationIndex>,
870 #[serde(rename = "FailoverContent")]
871 pub failover_content: Option<FailoverContent>,
872}
873
874#[skip_serializing_none]
876#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
877#[serde(default)]
878pub struct SegmentURL {
879 #[serde(rename = "@media")]
880 pub media: Option<String>, #[serde(rename = "@mediaRange")]
882 pub mediaRange: Option<String>,
883 #[serde(rename = "@index")]
884 pub index: Option<String>, #[serde(rename = "@indexRange")]
886 pub indexRange: Option<String>,
887}
888
889#[skip_serializing_none]
891#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
892#[serde(default)]
893pub struct SegmentList {
894 #[serde(rename = "@duration")]
896 pub duration: Option<u64>,
897 #[serde(rename = "@timescale")]
898 pub timescale: Option<u64>,
899 #[serde(rename = "@indexRange")]
900 pub indexRange: Option<String>,
901 #[serde(rename = "@indexRangeExact")]
902 pub indexRangeExact: Option<bool>,
903 #[serde(rename = "@xlink:href", alias = "@href")]
905 pub href: Option<String>,
906 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
907 pub actuate: Option<String>,
908 #[serde(rename = "@xlink:type", alias = "@type")]
909 pub sltype: Option<String>,
910 #[serde(rename = "@xlink:show", alias = "@show")]
911 pub show: Option<String>,
912 pub Initialization: Option<Initialization>,
913 pub SegmentTimeline: Option<SegmentTimeline>,
914 pub BitstreamSwitching: Option<BitstreamSwitching>,
915 #[serde(rename = "SegmentURL")]
916 pub segment_urls: Vec<SegmentURL>,
917}
918
919#[skip_serializing_none]
920#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
921#[serde(default)]
922pub struct Resync {
923 #[serde(rename = "@type")]
924 pub rtype: Option<String>,
925 #[serde(rename = "@dT")]
926 pub dT: Option<u64>,
927 #[serde(rename = "@dImax")]
928 pub dImax: Option<f64>,
929 #[serde(rename = "@dImin")]
930 pub dImin: Option<f64>,
931 #[serde(rename = "@marker")]
932 pub marker: Option<bool>,
933}
934
935#[skip_serializing_none]
937#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
938#[serde(default)]
939pub struct AudioChannelConfiguration {
940 #[serde(rename = "@schemeIdUri")]
941 pub schemeIdUri: String,
942 #[serde(rename = "@value")]
943 pub value: Option<String>,
944 #[serde(rename = "@id")]
945 pub id: Option<String>,
946}
947
948#[skip_serializing_none]
950#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
951#[serde(default)]
952pub struct Language {
953 #[serde(rename = "$text")]
954 pub content: Option<String>,
955}
956
957#[skip_serializing_none]
963#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
964#[serde(default)]
965pub struct Preselection {
966 #[serde(rename = "@id", default = "default_optstring_one")]
967 pub id: Option<String>,
968 #[serde(rename = "@preselectionComponents")]
971 pub preselectionComponents: String,
972 #[serde(rename = "@lang")]
973 pub lang: Option<String>,
974 #[serde(rename = "@audioSamplingRate")]
975 pub audioSamplingRate: Option<String>,
976 #[serde(rename = "@codecs")]
978 pub codecs: String,
979 #[serde(rename = "@selectionPriority")]
980 pub selectionPriority: Option<u64>,
981 #[serde(rename = "@tag")]
982 pub tag: String,
983 pub FramePacking: Vec<FramePacking>,
984 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
985 pub ContentProtection: Vec<ContentProtection>,
986 pub OutputProtection: Option<OutputProtection>,
987 #[serde(rename = "EssentialProperty")]
988 pub essential_property: Vec<EssentialProperty>,
989 #[serde(rename = "SupplementalProperty")]
990 pub supplemental_property: Vec<SupplementalProperty>,
991 pub InbandEventStream: Vec<InbandEventStream>,
992 pub Switching: Vec<Switching>,
993 #[serde(rename = "GroupLabel")]
995 pub group_label: Vec<Label>,
996 pub Label: Vec<Label>,
997 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
998 pub Resync: Option<Resync>,
1000 #[serde(rename = "Accessibility")]
1001 pub accessibilities: Vec<Accessibility>,
1002 #[serde(rename = "Role")]
1003 pub roles: Vec<Role>,
1004 #[serde(rename = "Rating")]
1005 pub ratings: Vec<Rating>,
1006 #[serde(rename = "Viewpoint")]
1007 pub viewpoints: Vec<Viewpoint>,
1008 #[serde(rename = "Language")]
1010 pub languages: Vec<Language>,
1011}
1012
1013#[skip_serializing_none]
1016#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1017#[serde(default)]
1018pub struct Rating {
1019 #[serde(rename = "@id")]
1020 pub id: Option<String>,
1021 #[serde(rename = "@schemeIdUri")]
1022 pub schemeIdUri: String,
1023 #[serde(rename = "@value")]
1024 pub value: Option<String>,
1025}
1026
1027#[skip_serializing_none]
1029#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1030#[serde(default)]
1031pub struct FramePacking {
1032 #[serde(rename = "@id")]
1033 pub id: Option<String>,
1034 #[serde(rename = "@schemeIdUri")]
1035 pub schemeIdUri: String,
1036 #[serde(rename = "@value")]
1037 pub value: Option<String>,
1038}
1039
1040#[skip_serializing_none]
1045#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1046#[serde(default)]
1047pub struct Switching {
1048 #[serde(rename = "@interval")]
1049 pub interval: Option<u64>,
1050 #[serde(rename = "@type")]
1052 pub stype: Option<String>,
1053}
1054
1055#[skip_serializing_none]
1057#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1058#[serde(default)]
1059pub struct Accessibility {
1060 #[serde(rename = "@schemeIdUri")]
1061 pub schemeIdUri: String,
1062 #[serde(rename = "@value")]
1063 pub value: Option<String>,
1064 #[serde(rename = "@id")]
1065 pub id: Option<String>,
1066}
1067
1068#[skip_serializing_none]
1070#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1071#[serde(default)]
1072pub struct Scope {
1073 #[serde(rename = "@schemeIdUri")]
1074 pub schemeIdUri: String,
1075 #[serde(rename = "@value")]
1076 pub value: Option<String>,
1077 #[serde(rename = "@id")]
1078 pub id: Option<String>,
1079}
1080
1081#[skip_serializing_none]
1083#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1084#[serde(default)]
1085pub struct SubRepresentation {
1086 #[serde(rename = "@level")]
1087 pub level: Option<u32>,
1088 #[serde(rename = "@dependencyLevel")]
1089 pub dependencyLevel: Option<String>,
1090 #[serde(rename = "@contentComponent")]
1092 pub contentComponent: Option<String>,
1093 #[serde(rename = "@mimeType")]
1094 pub mimeType: Option<String>,
1095 #[serde(rename = "@codecs")]
1097 pub codecs: Option<String>,
1098 #[serde(rename = "@contentType")]
1099 pub contentType: Option<String>,
1100 #[serde(rename = "@profiles")]
1101 pub profiles: Option<String>,
1102 #[serde(rename = "@segmentProfiles")]
1103 pub segmentProfiles: Option<String>,
1106 #[serde(rename = "@scanType")]
1108 pub scanType: Option<String>,
1109 #[serde(rename = "@frameRate")]
1110 pub frameRate: Option<String>, #[serde(rename = "@sar")]
1113 pub sar: Option<String>,
1114 #[serde(rename = "@bandwidth")]
1116 pub bandwidth: Option<u64>,
1117 #[serde(rename = "@audioSamplingRate")]
1118 pub audioSamplingRate: Option<String>,
1119 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1121 pub maxPlayoutRate: Option<f64>,
1122 #[serde(rename = "@codingDependency")]
1123 pub codingDependency: Option<bool>,
1124 #[serde(rename = "@width")]
1125 pub width: Option<u64>,
1126 #[serde(rename = "@height")]
1127 pub height: Option<u64>,
1128 #[serde(rename = "@startWithSAP")]
1129 pub startWithSAP: Option<u64>,
1130 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1131 pub maximumSAPPeriod: Option<f64>,
1132 pub FramePacking: Vec<FramePacking>,
1133 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1134 pub ContentProtection: Vec<ContentProtection>,
1135 pub OutputProtection: Option<OutputProtection>,
1136 #[serde(rename = "EssentialProperty")]
1137 pub essential_property: Vec<EssentialProperty>,
1138 #[serde(rename = "SupplementalProperty")]
1139 pub supplemental_property: Vec<SupplementalProperty>,
1140 pub InbandEventStream: Vec<InbandEventStream>,
1141 pub Switching: Vec<Switching>,
1142 #[serde(rename = "GroupLabel")]
1144 pub group_label: Vec<Label>,
1145 pub Label: Vec<Label>,
1146 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
1147 pub Resync: Option<Resync>,
1149}
1150
1151#[skip_serializing_none]
1156#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1157#[serde(default)]
1158pub struct Representation {
1159 #[serde(rename = "@id")]
1161 pub id: Option<String>,
1162 #[serde(rename = "@bandwidth")]
1164 pub bandwidth: Option<u64>,
1165 #[serde(rename = "@qualityRanking")]
1169 pub qualityRanking: Option<u8>,
1170 #[serde(rename = "@dependencyId")]
1174 pub dependencyId: Option<String>,
1175 #[serde(rename = "@associationId")]
1176 pub associationId: Option<String>,
1177 #[serde(rename = "@associationType")]
1178 pub associationType: Option<String>,
1179 #[serde(rename = "@mediaStreamStructureId")]
1180 pub mediaStreamStructureId: Option<String>,
1181 #[serde(rename = "@profiles")]
1182 pub profiles: Option<String>,
1183 #[serde(rename = "@width")]
1184 pub width: Option<u64>,
1185 #[serde(rename = "@height")]
1186 pub height: Option<u64>,
1187 #[serde(rename = "@sar")]
1189 pub sar: Option<String>,
1190 #[serde(rename = "@frameRate")]
1191 pub frameRate: Option<String>, #[serde(rename = "@audioSamplingRate")]
1193 pub audioSamplingRate: Option<String>,
1194 #[serde(rename = "@mimeType")]
1197 pub mimeType: Option<String>,
1198 #[serde(rename = "@segmentProfiles")]
1201 pub segmentProfiles: Option<String>,
1202 #[serde(rename = "@codecs")]
1205 pub codecs: Option<String>,
1206 #[serde(rename = "@containerProfiles")]
1207 pub containerProfiles: Option<String>,
1208 #[serde(rename = "@maximumSAPPeriod")]
1209 pub maximumSAPPeriod: Option<f64>,
1210 #[serde(rename = "@startWithSAP")]
1211 pub startWithSAP: Option<u64>,
1212 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1214 pub maxPlayoutRate: Option<f64>,
1215 #[serde(rename = "@codingDependency")]
1216 pub codingDependency: Option<bool>,
1217 #[serde(rename = "@scanType")]
1219 pub scanType: Option<String>,
1220 #[serde(rename = "@selectionPriority")]
1221 pub selectionPriority: Option<u64>,
1222 #[serde(rename = "@tag")]
1223 pub tag: Option<String>,
1224 #[serde(rename = "@contentType")]
1225 pub contentType: Option<String>,
1226 #[serde(rename = "@lang")]
1228 pub lang: Option<String>,
1229 #[serde(rename = "@sampleRate")]
1230 pub sampleRate: Option<u64>,
1231 #[serde(rename = "@numChannels")]
1232 pub numChannels: Option<u32>,
1233 #[serde(rename = "@xlink:href", alias = "@href")]
1234 pub href: Option<String>,
1235 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1236 pub actuate: Option<String>,
1237 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1238 pub scte214_supplemental_profiles: Option<String>,
1239 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1240 pub scte214_supplemental_codecs: Option<String>,
1241 pub FramePacking: Vec<FramePacking>,
1242 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1243 pub ContentProtection: Vec<ContentProtection>,
1244 pub OutputProtection: Option<OutputProtection>,
1245 #[serde(rename = "EssentialProperty")]
1246 pub essential_property: Vec<EssentialProperty>,
1247 #[serde(rename = "SupplementalProperty")]
1248 pub supplemental_property: Vec<SupplementalProperty>,
1249 pub InbandEventStream: Vec<InbandEventStream>,
1250 pub Switching: Vec<Switching>,
1251 #[serde(rename = "GroupLabel")]
1253 pub group_label: Vec<Label>,
1254 pub Label: Vec<Label>,
1255 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1256 pub Resync: Vec<Resync>,
1258 pub BaseURL: Vec<BaseURL>,
1259 pub SubRepresentation: Vec<SubRepresentation>,
1261 pub SegmentBase: Option<SegmentBase>,
1262 pub SegmentList: Option<SegmentList>,
1263 pub SegmentTemplate: Option<SegmentTemplate>,
1264 #[serde(rename = "RepresentationIndex")]
1265 pub representation_index: Option<RepresentationIndex>,
1266}
1267
1268#[skip_serializing_none]
1270#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1271#[serde(default)]
1272pub struct ContentComponent {
1273 #[serde(rename = "@id")]
1274 pub id: Option<String>,
1275 #[serde(rename = "@lang")]
1277 pub lang: Option<String>,
1278 #[serde(rename = "@contentType")]
1279 pub contentType: Option<String>,
1280 #[serde(rename = "@par")]
1281 pub par: Option<String>,
1282 #[serde(rename = "@tag")]
1283 pub tag: Option<String>,
1284 pub Accessibility: Vec<Accessibility>,
1285 pub Role: Vec<Role>,
1286 pub Rating: Vec<Rating>,
1287 pub Viewpoint: Vec<Viewpoint>,
1288}
1289
1290#[skip_serializing_none]
1292#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1293#[serde(default)]
1294pub struct CencPssh {
1295 #[serde(rename = "$text")]
1296 pub content: Option<String>,
1297}
1298
1299#[skip_serializing_none]
1301#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1302#[serde(default)]
1303pub struct Laurl {
1304 #[serde(rename = "@Lic_type")]
1305 pub lic_type: Option<String>,
1306 #[serde(rename = "$text")]
1307 pub content: Option<String>,
1308}
1309
1310#[skip_serializing_none]
1312#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1313#[serde(default)]
1314pub struct MsprPro {
1315 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
1316 pub xmlns: Option<String>,
1317 #[serde(rename = "$text")]
1318 pub content: Option<String>,
1319}
1320
1321#[skip_serializing_none]
1322#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1323#[serde(default)]
1324pub struct MsprIsEncrypted {
1325 #[serde(rename = "$text")]
1326 pub content: Option<String>,
1327}
1328
1329#[skip_serializing_none]
1330#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1331#[serde(default)]
1332pub struct MsprIVSize {
1333 #[serde(rename = "$text")]
1334 pub content: Option<String>,
1335}
1336
1337#[skip_serializing_none]
1338#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1339#[serde(default)]
1340pub struct MsprKid {
1341 #[serde(rename = "$text")]
1342 pub content: Option<String>,
1343}
1344
1345#[skip_serializing_none]
1346#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1347#[serde(default)]
1348pub struct OutputProtection {
1349 #[serde(rename = "@schemeIdUri")]
1350 pub schemeIdUri: String,
1351 #[serde(rename = "@value")]
1352 pub value: Option<String>,
1353 #[serde(rename = "@id")]
1354 pub id: Option<String>,
1355}
1356
1357#[skip_serializing_none]
1362#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1363#[serde(default)]
1364pub struct ContentProtection {
1365 #[serde(rename = "@robustness")]
1367 pub robustness: Option<String>,
1368 #[serde(rename = "@refId")]
1369 pub refId: Option<String>,
1370 #[serde(rename = "@ref")]
1372 pub r#ref: Option<String>,
1373 #[serde(rename = "@schemeIdUri")]
1375 pub schemeIdUri: String,
1376 #[serde(rename = "@value")]
1377 pub value: Option<String>,
1378 #[serde(rename = "@id")]
1379 pub id: Option<String>,
1380 #[serde(rename="cenc:pssh", alias="pssh")]
1382 pub cenc_pssh: Vec<CencPssh>,
1383 #[serde(rename = "@cenc:default_KID", alias = "@default_KID")]
1385 pub default_KID: Option<String>,
1386 #[serde(rename = "dashif:laurl", alias = "laurl")]
1388 pub laurl: Option<Laurl>,
1389 #[serde(rename = "clearkey:Laurl", alias = "Laurl")]
1393 pub clearkey_laurl: Option<Laurl>,
1394 #[serde(rename = "mspr:pro", alias = "pro")]
1396 pub msprpro: Option<MsprPro>,
1397 #[serde(rename = "mspr:IsEncrypted", alias = "IsEncrypted")]
1398 pub mspr_is_encrypted: Option<MsprIsEncrypted>,
1399 #[serde(rename = "mspr:IV_Size", alias = "IV_Size")]
1400 pub mspr_iv_size: Option<MsprIVSize>,
1401 #[serde(rename = "mspr:kid", alias = "kid")]
1402 pub mspr_kid: Option<MsprKid>,
1403}
1404
1405#[skip_serializing_none]
1411#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1412#[serde(default)]
1413pub struct Role {
1414 #[serde(rename = "@id")]
1415 pub id: Option<String>,
1416 #[serde(rename = "@schemeIdUri")]
1417 pub schemeIdUri: String,
1418 #[serde(rename = "@value")]
1419 pub value: Option<String>,
1420}
1421
1422#[skip_serializing_none]
1423#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1424#[serde(default)]
1425pub struct Viewpoint {
1426 #[serde(rename = "@id")]
1427 pub id: Option<String>,
1428 #[serde(rename = "@schemeIdUri")]
1429 pub schemeIdUri: String,
1430 #[serde(rename = "@value")]
1431 pub value: Option<String>,
1432}
1433
1434#[skip_serializing_none]
1435#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1436#[serde(default)]
1437pub struct Selection {
1438 #[serde(rename = "@dataEncoding")]
1439 pub dataEncoding: Option<String>,
1440 #[serde(rename = "@parameter")]
1441 pub parameter: Option<String>,
1442 #[serde(rename = "@data")]
1443 pub data: Option<String>,
1444}
1445
1446#[skip_serializing_none]
1447#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1448#[serde(default)]
1449pub struct SelectionInfo {
1450 #[serde(rename = "@selectionInfo")]
1451 pub selectionInfo: Option<String>,
1452 #[serde(rename = "@contactURL")]
1453 pub contactURL: Option<String>,
1454 pub Selection: Vec<Selection>,
1455}
1456
1457#[skip_serializing_none]
1464#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1465#[serde(default)]
1466pub struct Event {
1467 #[serde(rename = "@id")]
1468 pub id: Option<String>,
1469 #[serde(rename = "@presentationTime", default = "default_optu64_zero")]
1470 pub presentationTime: Option<u64>,
1471 #[serde(rename = "@presentationTimeOffset")]
1472 pub presentationTimeOffset: Option<u64>,
1473 #[serde(rename = "@duration")]
1474 pub duration: Option<u64>,
1475 #[serde(rename = "@timescale")]
1476 pub timescale: Option<u64>,
1477 #[serde(rename = "@contentEncoding")]
1480 pub contentEncoding: Option<String>,
1481 #[serde(rename = "@messageData")]
1484 pub messageData: Option<String>,
1485 pub SelectionInfo: Option<SelectionInfo>,
1486 #[cfg(feature = "scte35")]
1487 #[serde(rename = "scte35:Signal", alias="Signal")]
1488 #[cfg(feature = "scte35")]
1489 pub signal: Vec<Signal>,
1490 #[cfg(feature = "scte35")]
1491 #[serde(rename = "scte35:SpliceInfoSection", alias="SpliceInfoSection")]
1492 #[cfg(feature = "scte35")]
1493 pub splice_info_section: Vec<SpliceInfoSection>,
1494 #[serde(rename = "@value")]
1497 pub value: Option<String>,
1498 #[serde(rename = "$text")]
1501 pub content: Option<String>,
1502}
1503
1504#[skip_serializing_none]
1505#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1506#[serde(default)]
1507pub struct EventStream {
1508 #[serde(rename = "@xlink:href")]
1509 #[serde(alias = "@href")]
1510 pub href: Option<String>,
1511 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1512 pub actuate: Option<String>,
1513 #[serde(rename = "@messageData")]
1514 pub messageData: Option<String>,
1516 #[serde(rename = "@schemeIdUri")]
1517 pub schemeIdUri: String,
1518 #[serde(rename = "@value")]
1519 pub value: Option<String>,
1520 #[serde(rename = "@timescale")]
1521 pub timescale: Option<u64>,
1522 #[serde(rename = "@presentationTimeOffset")]
1523 pub presentationTimeOffset: Option<u64>,
1524 #[serde(rename = "Event")]
1525 pub event: Vec<Event>,
1526}
1527
1528#[skip_serializing_none]
1534#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1535#[serde(default)]
1536pub struct InbandEventStream {
1537 #[serde(rename = "@timescale")]
1538 pub timescale: Option<u64>,
1539 #[serde(rename = "@schemeIdUri")]
1540 pub schemeIdUri: String,
1541 #[serde(rename = "Event")]
1542 pub event: Vec<Event>,
1543 #[serde(rename = "@value")]
1544 pub value: Option<String>,
1545 #[serde(rename = "@xlink:href")]
1547 #[serde(alias = "@href")]
1548 pub href: Option<String>,
1549 #[serde(rename = "@xlink:actuate", alias = "@actuate")]
1550 pub actuate: Option<String>,
1551}
1552
1553#[skip_serializing_none]
1554#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1555#[serde(default)]
1556pub struct EssentialProperty {
1557 #[serde(rename = "@id")]
1558 pub id: Option<String>,
1559 #[serde(rename = "@schemeIdUri")]
1560 pub schemeIdUri: String,
1561 #[serde(rename = "@value")]
1562 pub value: Option<String>,
1563}
1564
1565#[skip_serializing_none]
1566#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1567#[serde(default)]
1568pub struct SupplementalProperty {
1569 #[serde(rename = "@id")]
1570 pub id: Option<String>,
1571 #[serde(rename = "@schemeIdUri")]
1572 pub schemeIdUri: String,
1573 #[serde(rename = "@value")]
1574 pub value: Option<String>,
1575 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1576 #[serde(rename(deserialize = "ContentIdentifier"))]
1577 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1578}
1579
1580#[skip_serializing_none]
1583#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1584#[serde(default)]
1585pub struct Label {
1586 #[serde(rename = "@id")]
1587 pub id: Option<String>,
1588 #[serde(rename = "@lang")]
1589 pub lang: Option<String>,
1590 #[serde(rename = "$text")]
1591 pub content: String,
1592}
1593
1594#[skip_serializing_none]
1602#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1603#[serde(default)]
1604pub struct AdaptationSet {
1605 #[serde(rename = "@id")]
1606 pub id: Option<String>,
1607 #[serde(rename = "@xlink:href", alias = "@href")]
1609 pub href: Option<String>,
1610 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1611 pub actuate: Option<String>,
1612 #[serde(rename = "@group")]
1613 pub group: Option<i64>,
1614 #[serde(rename = "@selectionPriority")]
1615 pub selectionPriority: Option<u64>,
1616 #[serde(rename = "@contentType")]
1618 pub contentType: Option<String>,
1619 #[serde(rename = "@profiles")]
1620 pub profiles: Option<String>,
1621 #[serde(rename = "@lang")]
1623 pub lang: Option<String>,
1624 #[serde(rename = "@sar")]
1626 pub sar: Option<String>,
1627 #[serde(rename = "@par")]
1629 pub par: Option<String>,
1630 #[serde(rename = "@scanType")]
1632 pub scanType: Option<String>,
1633 #[serde(rename = "@segmentAlignment")]
1634 pub segmentAlignment: Option<bool>,
1635 #[serde(rename = "@segmentProfiles")]
1636 pub segmentProfiles: Option<String>,
1639 #[serde(rename = "@subsegmentAlignment")]
1640 pub subsegmentAlignment: Option<bool>,
1641 #[serde(rename = "@subsegmentStartsWithSAP")]
1642 pub subsegmentStartsWithSAP: Option<u64>,
1643 #[serde(rename = "@bitstreamSwitching")]
1644 pub bitstreamSwitching: Option<bool>,
1645 #[serde(rename = "@audioSamplingRate")]
1646 pub audioSamplingRate: Option<String>,
1647 #[serde(rename = "@width")]
1648 pub width: Option<u64>,
1649 #[serde(rename = "@height")]
1650 pub height: Option<u64>,
1651 #[serde(rename = "@mimeType")]
1653 pub mimeType: Option<String>,
1654 #[serde(rename = "@codecs")]
1656 pub codecs: Option<String>,
1657 #[serde(rename = "@minBandwidth")]
1658 pub minBandwidth: Option<u64>,
1659 #[serde(rename = "@maxBandwidth")]
1660 pub maxBandwidth: Option<u64>,
1661 #[serde(rename = "@minWidth")]
1662 pub minWidth: Option<u64>,
1663 #[serde(rename = "@maxWidth")]
1664 pub maxWidth: Option<u64>,
1665 #[serde(rename = "@minHeight")]
1666 pub minHeight: Option<u64>,
1667 #[serde(rename = "@maxHeight")]
1668 pub maxHeight: Option<u64>,
1669 #[serde(rename = "@frameRate")]
1670 pub frameRate: Option<String>, #[serde(rename = "@minFrameRate")]
1672 pub minFrameRate: Option<String>, #[serde(rename = "@maxFrameRate")]
1674 pub maxFrameRate: Option<String>, #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1677 pub maxPlayoutRate: Option<f64>,
1678 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1679 pub maximumSAPPeriod: Option<f64>,
1680 #[serde(rename = "@startWithSAP")]
1681 pub startWithSAP: Option<u64>,
1682 #[serde(rename = "@codingDependency")]
1683 pub codingDependency: Option<bool>,
1684 pub FramePacking: Vec<FramePacking>,
1685 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1686 pub ContentProtection: Vec<ContentProtection>,
1687 #[serde(rename = "EssentialProperty")]
1689 pub essential_property: Vec<EssentialProperty>,
1690 #[serde(rename = "SupplementalProperty")]
1691 pub supplemental_property: Vec<SupplementalProperty>,
1692 pub InbandEventStream: Vec<InbandEventStream>,
1693 pub Switching: Vec<Switching>,
1694 pub GroupLabel: Vec<Label>,
1696 pub Label: Vec<Label>,
1697 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1698 pub Resync: Vec<Resync>,
1700 pub Accessibility: Vec<Accessibility>,
1701 pub Role: Vec<Role>,
1702 pub Rating: Vec<Rating>,
1703 pub Viewpoint: Vec<Viewpoint>,
1704 pub ContentComponent: Vec<ContentComponent>,
1705 pub BaseURL: Vec<BaseURL>,
1706 pub SegmentBase: Option<SegmentBase>,
1707 pub SegmentList: Option<SegmentList>,
1708 pub SegmentTemplate: Option<SegmentTemplate>,
1709 #[serde(rename = "Representation")]
1710 pub representations: Vec<Representation>,
1711 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1712 pub scte214_supplemental_profiles: Option<String>,
1713 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1714 pub scte214_supplemental_codecs: Option<String>,
1715}
1716
1717#[skip_serializing_none]
1722#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1723#[serde(default)]
1724pub struct AssetIdentifier {
1725 #[serde(rename = "@schemeIdUri")]
1726 pub schemeIdUri: String,
1727 #[serde(rename = "@value")]
1728 pub value: Option<String>,
1729 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1730 #[serde(rename(deserialize = "ContentIdentifier"))]
1731 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1732}
1733
1734#[skip_serializing_none]
1739#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1740#[serde(default)]
1741pub struct Subset {
1742 #[serde(rename = "@id")]
1743 pub id: Option<String>,
1744 #[serde(rename = "@contains",
1747 deserialize_with = "deserialize_xsd_uintvector",
1748 serialize_with = "serialize_xsd_uintvector",
1749 default)]
1750 pub contains: Vec<u64>,
1751}
1752
1753#[skip_serializing_none]
1756#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1757#[serde(default)]
1758pub struct Period {
1759 #[serde(rename = "@xlink:href", alias = "@href")]
1761 pub href: Option<String>,
1762
1763 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
1764 pub actuate: Option<String>,
1765
1766 #[serde(rename = "@id")]
1767 pub id: Option<String>,
1768
1769 #[serde(rename = "@start",
1771 serialize_with = "serialize_xs_duration",
1772 deserialize_with = "deserialize_xs_duration",
1773 default)]
1774 pub start: Option<Duration>,
1775
1776 #[serde(rename = "@duration",
1778 serialize_with = "serialize_xs_duration",
1779 deserialize_with = "deserialize_xs_duration",
1780 default)]
1781 pub duration: Option<Duration>,
1782
1783 #[serde(rename = "@bitstreamSwitching", default)]
1785 pub bitstreamSwitching: Option<bool>,
1786
1787 pub BaseURL: Vec<BaseURL>,
1788
1789 pub SegmentBase: Option<SegmentBase>,
1790
1791 pub SegmentList: Option<SegmentList>,
1792
1793 pub SegmentTemplate: Option<SegmentTemplate>,
1794
1795 #[serde(rename = "AssetIdentifier")]
1796 pub asset_identifier: Option<AssetIdentifier>,
1797
1798 #[serde(rename = "EventStream")]
1799 pub event_streams: Vec<EventStream>,
1800
1801 #[serde(rename = "ServiceDescription")]
1802 pub service_description: Vec<ServiceDescription>,
1803
1804 pub ContentProtection: Vec<ContentProtection>,
1805
1806 #[serde(rename = "AdaptationSet")]
1807 pub adaptations: Vec<AdaptationSet>,
1808
1809 #[serde(rename = "Subset")]
1810 pub subsets: Vec<Subset>,
1811
1812 #[serde(rename = "SupplementalProperty")]
1813 pub supplemental_property: Vec<SupplementalProperty>,
1814
1815 #[serde(rename = "EmptyAdaptationSet")]
1816 pub empty_adaptations: Vec<AdaptationSet>,
1817
1818 #[serde(rename = "GroupLabel")]
1819 pub group_label: Vec<Label>,
1820
1821 #[serde(rename = "Preselection")]
1822 pub pre_selections: Vec<Preselection>,
1823
1824 #[serde(rename = "EssentialProperty")]
1825 pub essential_property: Vec<EssentialProperty>,
1826}
1827
1828#[skip_serializing_none]
1829#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1830#[serde(default)]
1831pub struct Reporting {
1832 #[serde(rename = "@id")]
1833 pub id: Option<String>,
1834 #[serde(rename = "@schemeIdUri")]
1835 pub schemeIdUri: String,
1836 #[serde(rename = "@value")]
1837 pub value: Option<String>,
1838 #[serde(rename = "@dvb:reportingUrl", alias = "@reportingUrl")]
1839 pub reportingUrl: Option<String>,
1840 #[serde(rename = "@dvb:probability", alias = "@probability")]
1841 pub probability: Option<u64>,
1842}
1843
1844#[skip_serializing_none]
1845#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1846#[serde(default)]
1847pub struct Range {
1848 #[serde(rename = "@starttime",
1849 serialize_with = "serialize_xs_duration",
1850 deserialize_with = "deserialize_xs_duration",
1851 default)]
1852 pub starttime: Option<Duration>,
1853 #[serde(rename = "@duration",
1854 serialize_with = "serialize_xs_duration",
1855 deserialize_with = "deserialize_xs_duration",
1856 default)]
1857 pub duration: Option<Duration>,
1858}
1859
1860#[skip_serializing_none]
1861#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1862#[serde(default)]
1863pub struct Metrics {
1864 #[serde(rename = "@metrics")]
1865 pub metrics: String,
1866 pub Reporting: Vec<Reporting>,
1867 pub Range: Vec<Range>,
1868}
1869
1870#[skip_serializing_none]
1872#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1873#[serde(default)]
1874pub struct Latency {
1875 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1876 pub min: Option<f64>,
1877 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1878 pub max: Option<f64>,
1879 #[serde(rename = "@target", serialize_with="serialize_opt_xsd_double")]
1880 pub target: Option<f64>,
1881 #[serde(rename = "@referenceId")]
1882 pub referenceId: Option<String>,
1883}
1884
1885#[skip_serializing_none]
1887#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1888#[serde(default)]
1889pub struct PlaybackRate {
1890 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1891 pub min: Option<f64>,
1892 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1893 pub max: Option<f64>,
1894}
1895
1896#[skip_serializing_none]
1898#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1899#[serde(default)]
1900pub struct OperatingQuality {
1901 #[serde(default = "default_optstring_any")]
1902 pub mediaType: Option<String>,
1903 #[serde(rename = "@min")]
1904 pub min: Option<u64>,
1905 #[serde(rename = "@max")]
1906 pub max: Option<u64>,
1907 #[serde(rename = "@target")]
1908 pub target: Option<u64>,
1909 #[serde(rename = "@type")]
1910 pub _type: Option<String>,
1911 #[serde(rename = "@maxDifference")]
1912 pub maxDifference: Option<u64>,
1913}
1914
1915#[skip_serializing_none]
1917#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1918#[serde(default)]
1919pub struct OperatingBandwidth {
1920 #[serde(rename = "@mediaType", default = "default_optstring_any")]
1921 pub mediaType: Option<String>,
1922 #[serde(rename = "@min")]
1923 pub min: Option<u64>,
1924 #[serde(rename = "@max")]
1925 pub max: Option<u64>,
1926 #[serde(rename = "@target")]
1927 pub target: Option<u64>,
1928}
1929
1930#[skip_serializing_none]
1931#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1932#[serde(default)]
1933pub struct ContentSteering {
1934 #[serde(rename = "@defaultServiceLocation")]
1935 pub defaultServiceLocation: Option<String>,
1936 #[serde(rename = "@queryBeforeStart", default = "default_optbool_false")]
1937 pub queryBeforeStart: Option<bool>,
1938 #[serde(rename = "@clientRequirement", default = "default_optbool_true")]
1939 pub clientRequirement: Option<bool>,
1940}
1941
1942#[skip_serializing_none]
1943#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1944#[serde(default)]
1945pub struct CMCDParameters {
1946 #[serde(rename = "@version", default = "default_optu64_one")]
1947 pub version: Option<u64>,
1948 #[serde(rename = "@mode", default = "default_optstring_query")]
1949 pub mode: Option<String>,
1950 #[serde(rename = "@includeInRequests", default = "default_optstring_segment")]
1951 pub includeInRequests: Option<String>,
1952 #[serde(rename = "@keys")]
1953 pub keys: String,
1954 #[serde(rename = "@contentID")]
1955 pub contentID: Option<String>,
1956 #[serde(rename = "@sessionID")]
1957 pub sessionID: Option<String>,
1958}
1959
1960#[skip_serializing_none]
1962#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1963#[serde(default)]
1964pub struct ClientDataReporting {
1965 pub CMCDParameters: Vec<CMCDParameters>,
1966 #[serde(rename = "@serviceLocations")]
1967 pub serviceLocations: Option<String>,
1968 #[serde(rename = "@adaptationSets")]
1969 pub adaptationSets: Option<String>,
1970}
1971
1972#[skip_serializing_none]
1973#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1974#[serde(default)]
1975pub struct PlaybackRestrictions {
1976 #[serde(rename = "@skipAfter",
1977 serialize_with = "serialize_xs_duration",
1978 deserialize_with = "deserialize_xs_duration",
1979 default)]
1980 pub skipAfter: Option<Duration>,
1981}
1982
1983#[skip_serializing_none]
1984#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1985#[serde(default)]
1986pub struct ServiceDescription {
1987 #[serde(rename = "Scope")]
1988 pub scopes: Vec<Scope>,
1989 pub Latency: Vec<Latency>,
1990 pub PlaybackRate: Vec<PlaybackRate>,
1991 pub OperatingQuality: Vec<OperatingQuality>,
1992 pub OperatingBandwidth: Vec<OperatingBandwidth>,
1993 pub ContentSteering: Vec<ContentSteering>,
1994 pub ClientDataReporting: Vec<ClientDataReporting>,
1995 pub PlaybackRestrictions: Vec<PlaybackRestrictions>,
1996 #[serde(rename = "@id")]
1997 pub id: Option<String>,
1998}
1999
2000#[skip_serializing_none]
2002#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2003#[serde(default)]
2004pub struct UTCTiming {
2005 #[serde(rename = "@id")]
2006 pub id: Option<String>,
2007 #[serde(rename = "@schemeIdUri")]
2010 pub schemeIdUri: String,
2011 #[serde(rename = "@value")]
2012 pub value: Option<String>,
2013}
2014
2015#[skip_serializing_none]
2020#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2021#[serde(default)]
2022pub struct ProducerReferenceTime {
2023 #[serde(rename = "@id")]
2025 pub id: Option<String>,
2026 #[serde(rename = "@inband", default = "default_optbool_false")]
2027 pub inband: Option<bool>,
2028 #[serde(rename = "@presentationTime")]
2030 pub presentationTime: Option<u64>,
2031 #[serde(rename = "@type", default = "default_optstring_encoder")]
2032 pub prtType: Option<String>,
2033 #[serde(rename = "@wallClockTime",
2037 alias="@wallclockTime",
2038 deserialize_with = "deserialize_xs_datetime",
2039 default)]
2040 pub wallClockTime: Option<XsDatetime>,
2041 pub UTCTiming: Option<UTCTiming>,
2042}
2043
2044#[skip_serializing_none]
2045#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
2046#[serde(default)]
2047pub struct LeapSecondInformation {
2048 #[serde(rename = "@availabilityStartLeapOffset")]
2049 pub availabilityStartLeapOffset: Option<i64>,
2050 #[serde(rename = "@nextAvailabilityStartLeapOffset")]
2051 pub nextAvailabilityStartLeapOffset: Option<i64>,
2052 #[serde(rename = "@nextLeapChangeTime",
2053 deserialize_with = "deserialize_xs_datetime",
2054 default)]
2055 pub nextLeapChangeTime: Option<XsDatetime>,
2056}
2057
2058#[skip_serializing_none]
2065#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2066#[serde(default)]
2067pub struct PatchLocation {
2068 #[serde(rename = "@ttl", serialize_with="serialize_opt_xsd_double")]
2069 pub ttl: Option<f64>,
2070 #[serde(rename = "$text")]
2071 pub content: String,
2072}
2073
2074#[skip_serializing_none]
2076#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
2077#[serde(default)]
2078pub struct MPD {
2079 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
2080 pub xmlns: Option<String>,
2081 #[serde(rename = "@id")]
2082 pub id: Option<String>,
2083 #[serde(rename = "@profiles")]
2084 pub profiles: Option<String>,
2085 #[serde(rename = "@type")]
2088 pub mpdtype: Option<String>,
2089 #[serde(rename = "@availabilityStartTime",
2090 deserialize_with = "deserialize_xs_datetime",
2091 default)]
2092 pub availabilityStartTime: Option<XsDatetime>,
2093 #[serde(rename = "@availabilityEndTime",
2094 deserialize_with = "deserialize_xs_datetime",
2095 default)]
2096 pub availabilityEndTime: Option<XsDatetime>,
2097 #[serde(rename = "@publishTime",
2098 deserialize_with = "deserialize_xs_datetime",
2099 default)]
2100 pub publishTime: Option<XsDatetime>,
2101 #[serde(rename = "@mediaPresentationDuration",
2102 serialize_with = "serialize_xs_duration",
2103 deserialize_with = "deserialize_xs_duration",
2104 default)]
2105 pub mediaPresentationDuration: Option<Duration>,
2106 #[serde(rename = "@minimumUpdatePeriod",
2107 serialize_with = "serialize_xs_duration",
2108 deserialize_with = "deserialize_xs_duration",
2109 default)]
2110 pub minimumUpdatePeriod: Option<Duration>,
2111 #[serde(rename = "@minBufferTime",
2113 serialize_with = "serialize_xs_duration",
2114 deserialize_with = "deserialize_xs_duration",
2115 default)]
2116 pub minBufferTime: Option<Duration>,
2117 #[serde(rename = "@timeShiftBufferDepth",
2120 serialize_with = "serialize_xs_duration",
2121 deserialize_with = "deserialize_xs_duration",
2122 default)]
2123 pub timeShiftBufferDepth: Option<Duration>,
2124 #[serde(rename = "@suggestedPresentationDelay",
2126 serialize_with = "serialize_xs_duration",
2127 deserialize_with = "deserialize_xs_duration",
2128 default)]
2129 pub suggestedPresentationDelay: Option<Duration>,
2130 #[serde(rename = "@maxSegmentDuration",
2131 serialize_with = "serialize_xs_duration",
2132 deserialize_with = "deserialize_xs_duration",
2133 default)]
2134 pub maxSegmentDuration: Option<Duration>,
2135 #[serde(rename = "@maxSubsegmentDuration",
2136 serialize_with = "serialize_xs_duration",
2137 deserialize_with = "deserialize_xs_duration",
2138 default)]
2139 pub maxSubsegmentDuration: Option<Duration>,
2140 #[serialize_always]
2142 #[serde(rename="@xmlns:xsi", alias="@xsi", serialize_with="serialize_xsi_ns")]
2143 pub xsi: Option<String>,
2144 #[serde(alias = "@ext", rename = "@xmlns:ext")]
2145 pub ext: Option<String>,
2146 #[serialize_always]
2148 #[serde(rename="@xmlns:cenc", alias="@cenc", serialize_with="serialize_cenc_ns")]
2149 pub cenc: Option<String>,
2150 #[serialize_always]
2152 #[serde(rename="@xmlns:mspr", alias="@mspr", serialize_with="serialize_mspr_ns")]
2153 pub mspr: Option<String>,
2154 #[serialize_always]
2156 #[serde(rename="@xmlns:xlink", alias="@xlink", serialize_with="serialize_xlink_ns")]
2157 pub xlink: Option<String>,
2158 #[cfg(feature = "scte35")]
2161 #[serialize_always]
2162 #[serde(rename="@xmlns:scte35", alias="@scte35", serialize_with="scte35::serialize_scte35_ns")]
2163 pub scte35: Option<String>,
2164 #[serialize_always]
2167 #[serde(rename="@xmlns:dvb", alias="@dvb", serialize_with="serialize_dvb_ns")]
2168 pub dvb: Option<String>,
2169 #[serde(rename = "@xsi:schemaLocation", alias = "@schemaLocation")]
2170 pub schemaLocation: Option<String>,
2171 #[serde(alias = "@scte214", rename = "@xmlns:scte214")]
2173 pub scte214: Option<String>,
2174 pub ProgramInformation: Vec<ProgramInformation>,
2175 #[serde(rename = "BaseURL")]
2177 pub base_url: Vec<BaseURL>,
2178 #[serde(rename = "Location", default)]
2179 pub locations: Vec<Location>,
2180 pub PatchLocation: Vec<PatchLocation>,
2183 pub ServiceDescription: Vec<ServiceDescription>,
2184 pub ContentProtection: Vec<ContentProtection>,
2186 #[serde(rename = "Period", default)]
2187 pub periods: Vec<Period>,
2188 pub Metrics: Vec<Metrics>,
2189 #[serde(rename = "EssentialProperty")]
2190 pub essential_property: Vec<EssentialProperty>,
2191 #[serde(rename = "SupplementalProperty")]
2192 pub supplemental_property: Vec<SupplementalProperty>,
2193 pub UTCTiming: Vec<UTCTiming>,
2194 pub LeapSecondInformation: Option<LeapSecondInformation>,
2196}
2197
2198impl std::fmt::Display for MPD {
2199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2200 write!(f, "{}", quick_xml::se::to_string(self).map_err(|_| std::fmt::Error)?)
2201 }
2202}
2203
2204pub fn parse(xml: &str) -> Result<MPD, DashMpdError> {
2206 #[cfg(feature = "warn_ignored_elements")]
2207 {
2208 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2209 let _: MPD = serde_ignored::deserialize(xd, |path| {
2210 warn!("Unused XML element in manifest: {path}");
2211 }).map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2212 }
2213 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2214 let mpd: MPD = serde_path_to_error::deserialize(xd)
2215 .map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2216 Ok(mpd)
2217}
2218
2219
2220fn is_audio_codec(name: &str) -> bool {
2222 name.starts_with("mp4a") ||
2223 name.starts_with("aac") ||
2224 name.starts_with("vorbis") ||
2225 name.starts_with("opus") ||
2226 name.starts_with("ogg") ||
2227 name.starts_with("webm") ||
2228 name.starts_with("flac") ||
2229 name.starts_with("mp3") ||
2230 name.starts_with("mpeg") ||
2231 name.starts_with("3gpp") ||
2232 name.starts_with("wav") ||
2233 name.starts_with("ec-3") ||
2234 name.starts_with("ac-4") ||
2235 name.starts_with("dtsc") ||
2236 name.starts_with("aptx") ||
2237 name.starts_with("aiff") ||
2238 name.starts_with("mha1") }
2240
2241
2242pub fn is_audio_adaptation(a: &&AdaptationSet) -> bool {
2248 if let Some(codec) = &a.codecs {
2249 if is_audio_codec(codec) {
2250 return true;
2251 }
2252 }
2253 if let Some(ct) = &a.contentType {
2254 if ct == "audio" {
2255 return true;
2256 }
2257 }
2258 if let Some(mimetype) = &a.mimeType {
2259 if mimetype.starts_with("audio/") {
2260 return true;
2261 }
2262 }
2263 for r in a.representations.iter() {
2264 if let Some(ct) = &r.contentType {
2265 if ct == "audio" {
2266 return true;
2267 }
2268 }
2269 if let Some(mimetype) = &r.mimeType {
2270 if mimetype.starts_with("audio/") {
2271 return true;
2272 }
2273 }
2274 }
2275 false
2276}
2277
2278pub fn is_video_adaptation(a: &&AdaptationSet) -> bool {
2287 if is_audio_adaptation(a) {
2288 return false;
2289 }
2290 if let Some(ct) = &a.contentType {
2291 if ct == "video" {
2292 return true;
2293 }
2294 }
2295 if let Some(mimetype) = &a.mimeType {
2296 if mimetype.starts_with("video/") {
2297 return true;
2298 }
2299 }
2300 for r in a.representations.iter() {
2301 if let Some(ct) = &r.contentType {
2302 if ct == "video" {
2303 return true;
2304 }
2305 }
2306 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2309 return false;
2310 }
2311 if let Some(mimetype) = &r.mimeType {
2312 if mimetype.starts_with("video/") {
2313 return true;
2314 }
2315 }
2316 }
2317 false
2318}
2319
2320
2321fn is_subtitle_mimetype(mt: &str) -> bool {
2322 mt.eq("text/vtt") ||
2323 mt.eq("application/ttml+xml") ||
2324 mt.eq("application/x-sami")
2325
2326 }
2329
2330fn is_subtitle_codec(c: &str) -> bool {
2331 c == "wvtt" ||
2332 c == "c608" ||
2333 c == "stpp" ||
2334 c == "tx3g" ||
2335 c.starts_with("stpp.")
2336}
2337
2338pub fn is_subtitle_adaptation(a: &&AdaptationSet) -> bool {
2350 if a.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2351 return true;
2352 }
2353 if a.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2354 return true;
2355 }
2356 if a.codecs.as_deref().is_some_and(is_subtitle_codec) {
2357 return true;
2358 }
2359 for cc in a.ContentComponent.iter() {
2360 if cc.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2361 return true;
2362 }
2363 }
2364 for r in a.representations.iter() {
2365 if r.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2366 return true;
2367 }
2368 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2370 return true;
2371 }
2372 }
2373 false
2374}
2375
2376
2377#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2379pub enum SubtitleType {
2380 Vtt,
2382 Srt,
2384 Sub,
2386 Ass,
2388 Ttxt,
2390 Ttml,
2392 Sami,
2394 Wvtt,
2398 Stpp,
2400 Eia608,
2402 Unknown,
2403}
2404
2405fn subtitle_type_for_mimetype(mt: &str) -> Option<SubtitleType> {
2406 match mt {
2407 "text/vtt" => Some(SubtitleType::Vtt),
2408 "application/ttml+xml" => Some(SubtitleType::Ttml),
2409 "application/x-sami" => Some(SubtitleType::Sami),
2410 _ => None
2411 }
2412}
2413
2414pub fn subtitle_type(a: &&AdaptationSet) -> SubtitleType {
2415 if let Some(mimetype) = &a.mimeType {
2416 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2417 return st;
2418 }
2419 }
2420 if let Some(codecs) = &a.codecs {
2421 if codecs == "wvtt" {
2422 return SubtitleType::Wvtt;
2424 }
2425 if codecs == "c608" {
2426 return SubtitleType::Eia608;
2427 }
2428 if codecs == "tx3g" {
2429 return SubtitleType::Ttxt;
2430 }
2431 if codecs == "stpp" {
2432 return SubtitleType::Stpp;
2433 }
2434 if codecs.starts_with("stpp.") {
2435 return SubtitleType::Stpp;
2436 }
2437 }
2438 for r in a.representations.iter() {
2439 if let Some(mimetype) = &r.mimeType {
2440 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2441 return st;
2442 }
2443 }
2444 if let Some(codecs) = &r.codecs {
2445 if codecs == "wvtt" {
2446 return SubtitleType::Wvtt;
2447 }
2448 if codecs == "c608" {
2449 return SubtitleType::Eia608;
2450 }
2451 if codecs == "tx3g" {
2452 return SubtitleType::Ttxt;
2453 }
2454 if codecs == "stpp" {
2455 return SubtitleType::Stpp;
2456 }
2457 if codecs.starts_with("stpp.") {
2458 return SubtitleType::Stpp;
2459 }
2460 }
2461 }
2462 SubtitleType::Unknown
2463}
2464
2465
2466#[allow(dead_code)]
2467fn content_protection_type(cp: &ContentProtection) -> String {
2468 if let Some(v) = &cp.value {
2469 if v.eq("cenc") {
2470 return String::from("cenc");
2471 }
2472 if v.eq("Widevine") {
2473 return String::from("Widevine");
2474 }
2475 if v.eq("MSPR 2.0") {
2476 return String::from("PlayReady");
2477 }
2478 }
2479 let uri = &cp.schemeIdUri;
2481 let uri = uri.to_lowercase();
2482 if uri.eq("urn:mpeg:dash:mp4protection:2011") {
2483 return String::from("cenc");
2484 }
2485 if uri.eq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed") {
2486 return String::from("Widevine");
2487 }
2488 if uri.eq("urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95") {
2489 return String::from("PlayReady");
2490 }
2491 if uri.eq("urn:uuid:94ce86fb-07ff-4f43-adb8-93d2fa968ca2") {
2492 return String::from("FairPlay");
2493 }
2494 if uri.eq("urn:uuid:3ea8778f-7742-4bf9-b18b-e834b2acbd47") {
2495 return String::from("Clear Key AES-128");
2496 }
2497 if uri.eq("urn:uuid:be58615b-19c4-4684-88b3-c8c57e99e957") {
2498 return String::from("Clear Key SAMPLE-AES");
2499 }
2500 if uri.eq("urn:uuid:adb41c24-2dbf-4a6d-958b-4457c0d27b95") {
2501 return String::from("Nagra");
2502 }
2503 if uri.eq("urn:uuid:5e629af5-38da-4063-8977-97ffbd9902d4") {
2504 return String::from("Marlin");
2505 }
2506 if uri.eq("urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb") {
2507 return String::from("Adobe PrimeTime");
2508 }
2509 if uri.eq("urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b") {
2510 return String::from("W3C Common PSSH box");
2511 }
2512 if uri.eq("urn:uuid:80a6be7e-1448-4c37-9e70-d5aebe04c8d2") {
2513 return String::from("Irdeto Content Protection");
2514 }
2515 if uri.eq("urn:uuid:3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c") {
2516 return String::from("WisePlay-ChinaDRM");
2517 }
2518 if uri.eq("urn:uuid:616c7469-6361-7374-2d50-726f74656374") {
2519 return String::from("Alticast");
2520 }
2521 if uri.eq("urn:uuid:6dd8b3c3-45f4-4a68-bf3a-64168d01a4a6") {
2522 return String::from("ABV DRM");
2523 }
2524 if uri.eq("urn:mpeg:dash:sea:2012") {
2526 return String::from("SEA");
2527 }
2528 String::from("<unknown>")
2529}
2530
2531
2532fn check_segment_template_duration(
2533 st: &SegmentTemplate,
2534 max_seg_duration: &Duration,
2535 outer_timescale: u64) -> Vec<String>
2536{
2537 let mut errors = Vec::new();
2538 if let Some(timeline) = &st.SegmentTimeline {
2539 for s in &timeline.segments {
2540 let sd = s.d / st.timescale.unwrap_or(outer_timescale);
2541 if sd > max_seg_duration.as_secs() {
2542 errors.push(String::from("SegmentTimeline has segment@d > @maxSegmentDuration"));
2543 }
2544 }
2545 }
2546 errors
2547}
2548
2549fn check_segment_template_conformity(st: &SegmentTemplate) -> Vec<String> {
2550 let mut errors = Vec::new();
2551 if let Some(md) = &st.media {
2552 if !valid_url_p(md) {
2553 errors.push(format!("invalid URL {md}"));
2554 }
2555 if md.contains("$Number$") && md.contains("$Time") {
2556 errors.push(String::from("both $Number$ and $Time$ are used in media template URL"));
2557 }
2558 }
2559 if let Some(init) = &st.initialization {
2560 if !valid_url_p(init) {
2561 errors.push(format!("invalid URL {init}"));
2562 }
2563 if init.contains("$Number") {
2564 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2565 }
2566 if init.contains("$Time") {
2567 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2568 }
2569 }
2570 if st.duration.is_some() && st.SegmentTimeline.is_some() {
2571 errors.push(String::from("both SegmentTemplate.duration and SegmentTemplate.SegmentTimeline present"));
2572 }
2573 errors
2574}
2575
2576
2577fn valid_url_p(u: &str) -> bool {
2580 use url::ParseError;
2581
2582 match Url::parse(u) {
2583 Ok(url) => {
2584 url.scheme() == "https" ||
2585 url.scheme() == "http" ||
2586 url.scheme() == "ftp" ||
2587 url.scheme() == "file" ||
2588 url.scheme() == "data"
2589 },
2590 Err(ParseError::RelativeUrlWithoutBase) => true,
2591 Err(_) => false,
2592 }
2593}
2594
2595pub fn check_conformity(mpd: &MPD) -> Vec<String> {
2597 let mut errors = Vec::new();
2598
2599 for p in &mpd.periods {
2602 if p.adaptations.is_empty() {
2603 errors.push(format!("Period with @id {} contains no AdaptationSet elements",
2604 p.id.clone().unwrap_or(String::from("<unspecified>"))));
2605 }
2606 for a in &p.adaptations {
2607 if let Some(mh) = a.maxHeight {
2608 if let Some(mr) = a.representations.iter().max_by_key(|r| r.height.unwrap_or(0)) {
2609 if mr.height.unwrap_or(0) > mh {
2610 errors.push(String::from("invalid @maxHeight on AdaptationSet"));
2611 }
2612 }
2613 }
2614 }
2615 }
2616 for p in &mpd.periods {
2619 for a in &p.adaptations {
2620 if let Some(mw) = a.maxWidth {
2621 if let Some(mr) = a.representations.iter().max_by_key(|r| r.width.unwrap_or(0)) {
2622 if mr.width.unwrap_or(0) > mw {
2623 errors.push(String::from("invalid @maxWidth on AdaptationSet"));
2624 }
2625 }
2626 }
2627 }
2628 }
2629 for p in &mpd.periods {
2632 for a in &p.adaptations {
2633 if let Some(mb) = a.maxBandwidth {
2634 if let Some(mr) = a.representations.iter().max_by_key(|r| r.bandwidth.unwrap_or(0)) {
2635 if mr.bandwidth.unwrap_or(0) > mb {
2636 errors.push(String::from("invalid @maxBandwidth on AdaptationSet"));
2637 }
2638 }
2639 }
2640 }
2641 }
2642 if let Some(max_seg_duration) = mpd.maxSegmentDuration {
2644 for p in &mpd.periods {
2645 for a in &p.adaptations {
2646 let mut outer_timescale = 1;
2657 if let Some(st) = &a.SegmentTemplate {
2658 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2659 .into_iter()
2660 .for_each(|msg| errors.push(msg));
2661 if let Some(ots) = st.timescale {
2662 outer_timescale = ots;
2663 }
2664 }
2665 for r in &a.representations {
2666 if let Some(st) = &r.SegmentTemplate {
2667 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2668 .into_iter()
2669 .for_each(|msg| errors.push(msg));
2670 }
2671 }
2672 }
2673 }
2674 }
2675
2676 for bu in &mpd.base_url {
2677 if !valid_url_p(&bu.base) {
2678 errors.push(format!("invalid URL {}", &bu.base));
2679 }
2680 }
2681 for p in &mpd.periods {
2682 for bu in &p.BaseURL {
2683 if !valid_url_p(&bu.base) {
2684 errors.push(format!("invalid URL {}", &bu.base));
2685 }
2686 }
2687 for a in &p.adaptations {
2688 for bu in &a.BaseURL {
2689 if !valid_url_p(&bu.base) {
2690 errors.push(format!("invalid URL {}", &bu.base));
2691 }
2692 }
2693 if let Some(st) = &a.SegmentTemplate {
2694 check_segment_template_conformity(st)
2695 .into_iter()
2696 .for_each(|msg| errors.push(msg));
2697 }
2698 for r in &a.representations {
2699 for bu in &r.BaseURL {
2700 if !valid_url_p(&bu.base) {
2701 errors.push(format!("invalid URL {}", &bu.base));
2702 }
2703 }
2704 if let Some(sb) = &r.SegmentBase {
2705 if let Some(init) = &sb.Initialization {
2706 if let Some(su) = &init.sourceURL {
2707 if !valid_url_p(su) {
2708 errors.push(format!("invalid URL {su}"));
2709 }
2710 if su.contains("$Number") {
2711 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2712 }
2713 if su.contains("$Time") {
2714 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2715 }
2716 }
2717 }
2718 if let Some(ri) = &sb.representation_index {
2719 if let Some(su) = &ri.sourceURL {
2720 if !valid_url_p(su) {
2721 errors.push(format!("invalid URL {su}"));
2722 }
2723 }
2724 }
2725 }
2726 if let Some(sl) = &r.SegmentList {
2727 if let Some(hr) = &sl.href {
2728 if !valid_url_p(hr) {
2729 errors.push(format!("invalid URL {hr}"));
2730 }
2731 }
2732 if let Some(init) = &sl.Initialization {
2733 if let Some(su) = &init.sourceURL {
2734 if !valid_url_p(su) {
2735 errors.push(format!("invalid URL {su}"));
2736 }
2737 if su.contains("$Number") {
2738 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2739 }
2740 if su.contains("$Time") {
2741 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2742 }
2743 }
2744 }
2745 for su in &sl.segment_urls {
2746 if let Some(md) = &su.media {
2747 if !valid_url_p(md) {
2748 errors.push(format!("invalid URL {md}"));
2749 }
2750 }
2751 if let Some(ix) = &su.index {
2752 if !valid_url_p(ix) {
2753 errors.push(format!("invalid URL {ix}"));
2754 }
2755 }
2756 }
2757 }
2758 if let Some(st) = &r.SegmentTemplate {
2759 check_segment_template_conformity(st)
2760 .into_iter()
2761 .for_each(|msg| errors.push(msg));
2762 }
2763 }
2764 }
2765 }
2766 for pi in &mpd.ProgramInformation {
2767 if let Some(u) = &pi.moreInformationURL {
2768 if !valid_url_p(u) {
2769 errors.push(format!("invalid URL {u}"));
2770 }
2771 }
2772 }
2773 errors
2774}
2775
2776#[cfg(test)]
2777mod tests {
2778 use proptest::prelude::*;
2779
2780 proptest! {
2781 #[test]
2782 fn doesnt_crash(s in "\\PC*") {
2783 let _ = super::parse_xs_duration(&s);
2784 let _ = super::parse_xs_datetime(&s);
2785 }
2786 }
2787
2788 #[test]
2789 fn test_parse_xs_duration() {
2790 use std::time::Duration;
2791 use super::parse_xs_duration;
2792
2793 assert!(parse_xs_duration("").is_err());
2794 assert!(parse_xs_duration("foobles").is_err());
2795 assert!(parse_xs_duration("P").is_err());
2796 assert!(parse_xs_duration("PW").is_err());
2797 assert!(parse_xs_duration("-PT4.5S").is_err());
2799 assert!(parse_xs_duration("1Y2M3DT4H5M6S").is_err()); assert_eq!(parse_xs_duration("PT3H11M53S").ok(), Some(Duration::new(11513, 0)));
2801 assert_eq!(parse_xs_duration("PT42M30S").ok(), Some(Duration::new(2550, 0)));
2802 assert_eq!(parse_xs_duration("PT30M38S").ok(), Some(Duration::new(1838, 0)));
2803 assert_eq!(parse_xs_duration("PT0H10M0.00S").ok(), Some(Duration::new(600, 0)));
2804 assert_eq!(parse_xs_duration("PT1.5S").ok(), Some(Duration::new(1, 500_000_000)));
2805 assert_eq!(parse_xs_duration("PT1.500S").ok(), Some(Duration::new(1, 500_000_000)));
2806 assert_eq!(parse_xs_duration("PT1.500000000S").ok(), Some(Duration::new(1, 500_000_000)));
2807 assert_eq!(parse_xs_duration("PT0S").ok(), Some(Duration::new(0, 0)));
2808 assert_eq!(parse_xs_duration("PT0.001S").ok(), Some(Duration::new(0, 1_000_000)));
2809 assert_eq!(parse_xs_duration("PT0.00100S").ok(), Some(Duration::new(0, 1_000_000)));
2810 assert_eq!(parse_xs_duration("PT344S").ok(), Some(Duration::new(344, 0)));
2811 assert_eq!(parse_xs_duration("PT634.566S").ok(), Some(Duration::new(634, 566_000_000)));
2812 assert_eq!(parse_xs_duration("PT72H").ok(), Some(Duration::new(72*60*60, 0)));
2813 assert_eq!(parse_xs_duration("PT0H0M30.030S").ok(), Some(Duration::new(30, 30_000_000)));
2814 assert_eq!(parse_xs_duration("PT1004199059S").ok(), Some(Duration::new(1004199059, 0)));
2815 assert_eq!(parse_xs_duration("P0Y20M0D").ok(), Some(Duration::new(51840000, 0)));
2816 assert_eq!(parse_xs_duration("PT1M30.5S").ok(), Some(Duration::new(90, 500_000_000)));
2817 assert_eq!(parse_xs_duration("PT10M10S").ok(), Some(Duration::new(610, 0)));
2818 assert_eq!(parse_xs_duration("PT1H0.040S").ok(), Some(Duration::new(3600, 40_000_000)));
2819 assert_eq!(parse_xs_duration("PT00H03M30SZ").ok(), Some(Duration::new(210, 0)));
2820 assert_eq!(parse_xs_duration("PT3.14159S").ok(), Some(Duration::new(3, 141_590_000)));
2821 assert_eq!(parse_xs_duration("PT3.14159265S").ok(), Some(Duration::new(3, 141_592_650)));
2822 assert_eq!(parse_xs_duration("PT3.141592653S").ok(), Some(Duration::new(3, 141_592_653)));
2823 assert_eq!(parse_xs_duration("PT3.141592653897S").ok(), Some(Duration::new(3, 141_592_653)));
2825 assert_eq!(parse_xs_duration("P0W").ok(), Some(Duration::new(0, 0)));
2826 assert_eq!(parse_xs_duration("P26W").ok(), Some(Duration::new(15724800, 0)));
2827 assert_eq!(parse_xs_duration("P52W").ok(), Some(Duration::new(31449600, 0)));
2828 assert_eq!(parse_xs_duration("P10D").ok(), Some(Duration::new(864000, 0)));
2829 assert_eq!(parse_xs_duration("P0Y").ok(), Some(Duration::new(0, 0)));
2830 assert_eq!(parse_xs_duration("P1Y").ok(), Some(Duration::new(31536000, 0)));
2831 assert_eq!(parse_xs_duration("P1Y0W0S").ok(), Some(Duration::new(31536000, 0)));
2832 assert_eq!(parse_xs_duration("PT4H").ok(), Some(Duration::new(14400, 0)));
2833 assert_eq!(parse_xs_duration("+PT4H").ok(), Some(Duration::new(14400, 0)));
2834 assert_eq!(parse_xs_duration("PT0004H").ok(), Some(Duration::new(14400, 0)));
2835 assert_eq!(parse_xs_duration("PT4H0M").ok(), Some(Duration::new(14400, 0)));
2836 assert_eq!(parse_xs_duration("PT4H0S").ok(), Some(Duration::new(14400, 0)));
2837 assert_eq!(parse_xs_duration("P23DT23H").ok(), Some(Duration::new(2070000, 0)));
2838 assert_eq!(parse_xs_duration("P0Y0M0DT0H4M20.880S").ok(), Some(Duration::new(260, 880_000_000)));
2839 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6.7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2840 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6,7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2841
2842 }
2846
2847 #[test]
2848 fn test_serialize_xs_duration() {
2849 use std::time::Duration;
2850 use super::MPD;
2851
2852 fn serialized_xs_duration(d: Duration) -> String {
2853 let mpd = MPD {
2854 minBufferTime: Some(d),
2855 ..Default::default()
2856 };
2857 let xml = mpd.to_string();
2858 let doc = roxmltree::Document::parse(&xml).unwrap();
2859 String::from(doc.root_element().attribute("minBufferTime").unwrap())
2860 }
2861
2862 assert_eq!("PT0S", serialized_xs_duration(Duration::new(0, 0)));
2863 assert_eq!("PT0.001S", serialized_xs_duration(Duration::new(0, 1_000_000)));
2864 assert_eq!("PT42S", serialized_xs_duration(Duration::new(42, 0)));
2865 assert_eq!("PT1.5S", serialized_xs_duration(Duration::new(1, 500_000_000)));
2866 assert_eq!("PT30.03S", serialized_xs_duration(Duration::new(30, 30_000_000)));
2867 assert_eq!("PT1M30.5S", serialized_xs_duration(Duration::new(90, 500_000_000)));
2868 assert_eq!("PT5M44S", serialized_xs_duration(Duration::new(344, 0)));
2869 assert_eq!("PT42M30S", serialized_xs_duration(Duration::new(2550, 0)));
2870 assert_eq!("PT30M38S", serialized_xs_duration(Duration::new(1838, 0)));
2871 assert_eq!("PT10M10S", serialized_xs_duration(Duration::new(610, 0)));
2872 assert_eq!("PT1H0M0.04S", serialized_xs_duration(Duration::new(3600, 40_000_000)));
2873 assert_eq!("PT3H11M53S", serialized_xs_duration(Duration::new(11513, 0)));
2874 assert_eq!("PT4H0M0S", serialized_xs_duration(Duration::new(14400, 0)));
2875 }
2876
2877 #[test]
2878 fn test_parse_xs_datetime() {
2879 use chrono::{DateTime, NaiveDate};
2880 use chrono::offset::Utc;
2881 use super::parse_xs_datetime;
2882
2883 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2884 .unwrap()
2885 .and_hms_opt(1, 3, 2)
2886 .unwrap();
2887 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02Z").ok(),
2888 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2889 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2890 .unwrap()
2891 .and_hms_nano_opt(1, 3, 2, 958*1000*1000)
2892 .unwrap();
2893 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02.958Z").ok(),
2894 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2895 }
2896}