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 std::cell::OnceCell;
91use serde::{Serialize, Serializer, Deserialize};
92use serde::de;
93use serde_with::skip_serializing_none;
94use regex::Regex;
95use std::time::Duration;
96use chrono::DateTime;
97use url::Url;
98#[allow(unused_imports)]
99use tracing::warn;
100
101
102pub type XsDatetime = DateTime<chrono::offset::Utc>;
105
106#[derive(thiserror::Error, Debug)]
107#[non_exhaustive]
108pub enum DashMpdError {
109 #[error("parse error {0:?}")]
110 Parsing(String),
111 #[error("invalid Duration: {0:?}")]
112 InvalidDuration(String),
113 #[error("invalid DateTime: {0:?}")]
114 InvalidDateTime(String),
115 #[error("invalid media stream: {0:?}")]
116 UnhandledMediaStream(String),
117 #[error("I/O error {1} ({0:?})")]
118 Io(#[source] std::io::Error, String),
119 #[error("network error {0:?}")]
120 Network(String),
121 #[error("network timeout: {0:?}")]
122 NetworkTimeout(String),
123 #[error("network connection: {0:?}")]
124 NetworkConnect(String),
125 #[error("muxing error {0:?}")]
126 Muxing(String),
127 #[error("decryption error {0:?}")]
128 Decrypting(String),
129 #[error("{0:?}")]
130 Other(String),
131}
132
133
134fn serialize_xsd_double<S>(xsd: &f64, serializer: S) -> Result<S::Ok, S::Error>
139where
140 S: Serializer,
141{
142 let formatted = if xsd.is_nan() {
143 String::from("NaN")
144 } else if xsd.is_infinite() {
145 if xsd.is_sign_positive() {
146 String::from("INF")
148 } else {
149 String::from("-INF")
150 }
151 } else {
152 xsd.to_string()
153 };
154 serializer.serialize_str(&formatted)
155}
156
157fn serialize_opt_xsd_double<S>(oxsd: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
159where
160 S: Serializer,
161{
162 if let Some(xsd) = oxsd {
163 serialize_xsd_double(xsd, serializer)
164 } else {
165 serializer.serialize_none()
167 }
168}
169
170
171fn parse_xs_duration(s: &str) -> Result<Duration, DashMpdError> {
187 use std::cmp::min;
188 let xs_duration_regex = OnceCell::new();
189
190 match xs_duration_regex.get_or_init(
191 || Regex::new(concat!(r"^(?P<sign>[+-])?P",
192 r"(?:(?P<years>\d+)Y)?",
193 r"(?:(?P<months>\d+)M)?",
194 r"(?:(?P<weeks>\d+)W)?",
195 r"(?:(?P<days>\d+)D)?",
196 r"(?:(?P<hastime>T)", r"(?:(?P<hours>\d+)H)?",
198 r"(?:(?P<minutes>\d+)M)?",
199 r"(?:(?P<seconds>\d+)(?:(?P<nanoseconds>[.,]\d+)?)S)?",
200 r")?")).unwrap()).captures(s)
201 {
202 Some(m) => {
203 if m.name("hastime").is_none() &&
204 m.name("years").is_none() &&
205 m.name("months").is_none() &&
206 m.name("weeks").is_none() &&
207 m.name("days").is_none() {
208 return Err(DashMpdError::InvalidDuration("empty".to_string()));
209 }
210 let mut secs: u64 = 0;
211 let mut nsecs: u32 = 0;
212 if let Some(nano) = m.name("nanoseconds") {
213 let lim = min(nano.as_str().len(), 9 + ".".len());
216 if let Some(ss) = &nano.as_str().get(1..lim) {
217 let padded = format!("{ss:0<9}");
218 nsecs = padded.parse::<u32>()
219 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
220 }
221 }
222 if let Some(mseconds) = m.name("seconds") {
223 let seconds = mseconds.as_str().parse::<u64>()
224 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
225 secs += seconds;
226 }
227 if let Some(mminutes) = m.name("minutes") {
228 let minutes = mminutes.as_str().parse::<u64>()
229 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
230 secs += minutes * 60;
231 }
232 if let Some(mhours) = m.name("hours") {
233 let hours = mhours.as_str().parse::<u64>()
234 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
235 secs += hours * 60 * 60;
236 }
237 if let Some(mdays) = m.name("days") {
238 let days = mdays.as_str().parse::<u64>()
239 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
240 secs += days * 60 * 60 * 24;
241 }
242 if let Some(mweeks) = m.name("weeks") {
243 let weeks = mweeks.as_str().parse::<u64>()
244 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
245 secs += weeks * 60 * 60 * 24 * 7;
246 }
247 if let Some(mmonths) = m.name("months") {
248 let months = mmonths.as_str().parse::<u64>()
249 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
250 secs += months * 60 * 60 * 24 * 30;
251 }
252 if let Some(myears) = m.name("years") {
253 let years = myears.as_str().parse::<u64>()
254 .map_err(|_| DashMpdError::InvalidDuration(String::from(s)))?;
255 secs += years * 60 * 60 * 24 * 365;
256 }
257 if let Some(msign) = m.name("sign") {
258 if msign.as_str() == "-" {
259 return Err(DashMpdError::InvalidDuration("can't represent negative durations".to_string()));
260 }
261 }
262 Ok(Duration::new(secs, nsecs))
263 },
264 None => Err(DashMpdError::InvalidDuration(String::from("couldn't parse XS duration"))),
265 }
266}
267
268
269fn deserialize_xs_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
317where
318 D: de::Deserializer<'de>,
319{
320 match <Option<String>>::deserialize(deserializer) {
321 Ok(optstring) => match optstring {
322 Some(xs) => match parse_xs_duration(&xs) {
323 Ok(d) => Ok(Some(d)),
324 Err(e) => Err(de::Error::custom(e)),
325 },
326 None => Ok(None),
327 },
328 Err(_) => Ok(None),
330 }
331}
332
333fn serialize_xs_duration<S>(oxs: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
341where
342 S: Serializer,
343{
344 if let Some(xs) = oxs {
345 let seconds = xs.as_secs();
346 let nanos = xs.subsec_nanos();
347 let minutes = seconds / 60;
348 let hours = if minutes > 59 { minutes / 60 } else { 0 };
349 let fractional_maybe = if nanos > 0 {
350 format!(".{nanos:09}").trim_end_matches('0').to_string()
351 } else {
352 "".to_string()
353 };
354 let formatted_duration = if hours > 0 {
355 let mins = minutes % 60;
356 let secs = seconds % 60;
357 format!("PT{hours}H{mins}M{secs}{fractional_maybe}S")
358 } else if minutes > 0 {
359 let secs = seconds % 60;
360 format!("PT{minutes}M{secs}{fractional_maybe}S")
361 } else {
362 format!("PT{seconds}{fractional_maybe}S")
363 };
364 serializer.serialize_str(&formatted_duration)
365 } else {
366 serializer.serialize_none()
368 }
369}
370
371
372fn parse_xs_datetime(s: &str) -> Result<XsDatetime, DashMpdError> {
378 use iso8601::Date;
379 use chrono::{LocalResult, NaiveDate, TimeZone};
380 use num_traits::cast::FromPrimitive;
381 match DateTime::<chrono::offset::FixedOffset>::parse_from_rfc3339(s) {
382 Ok(dt) => Ok(dt.into()),
383 Err(_) => match iso8601::datetime(s) {
384 Ok(dt) => {
385 let nd = match dt.date {
386 Date::YMD { year, month, day } =>
387 NaiveDate::from_ymd_opt(year, month, day)
388 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
389 Date::Week { year, ww, d } => {
390 let d = chrono::Weekday::from_u32(d)
391 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
392 NaiveDate::from_isoywd_opt(year, ww, d)
393 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
394 },
395 Date::Ordinal { year, ddd } =>
396 NaiveDate::from_yo_opt(year, ddd)
397 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?,
398 };
399 let nd = nd.and_hms_nano_opt(dt.time.hour, dt.time.minute, dt.time.second, dt.time.millisecond*1000*1000)
400 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?;
401 let tz_secs = dt.time.tz_offset_hours * 3600 + dt.time.tz_offset_minutes * 60;
402 match chrono::FixedOffset::east_opt(tz_secs)
403 .ok_or(DashMpdError::InvalidDateTime(s.to_string()))?
404 .from_local_datetime(&nd)
405 {
406 LocalResult::Single(local) => Ok(local.with_timezone(&chrono::Utc)),
407 _ => Err(DashMpdError::InvalidDateTime(s.to_string())),
408 }
409 },
410 Err(_) => Err(DashMpdError::InvalidDateTime(s.to_string())),
411 }
412 }
413}
414
415fn deserialize_xs_datetime<'de, D>(deserializer: D) -> Result<Option<XsDatetime>, D::Error>
417where
418 D: de::Deserializer<'de>,
419{
420 match <Option<String>>::deserialize(deserializer) {
421 Ok(optstring) => match optstring {
422 Some(xs) => match parse_xs_datetime(&xs) {
423 Ok(d) => Ok(Some(d)),
424 Err(e) => Err(de::Error::custom(e)),
425 },
426 None => Ok(None),
427 },
428 Err(_) => Ok(None),
430 }
431}
432
433fn serialize_xsd_uintvector<S>(v: &Vec<u64>, serializer: S) -> Result<S::Ok, S::Error>
436where
437 S: Serializer,
438{
439 let mut formatted = String::new();
440 for u in v {
441 formatted += &format!("{u} ");
442 }
443 serializer.serialize_str(&formatted)
444}
445
446fn deserialize_xsd_uintvector<'de, D>(deserializer: D) -> Result<Vec<u64>, D::Error>
447where
448 D: de::Deserializer<'de>,
449{
450 let s = String::deserialize(deserializer)?;
451 let mut out = Vec::<u64>::new();
452 for uint64_str in s.split_whitespace() {
453 match uint64_str.parse::<u64>() {
454 Ok(val) => out.push(val),
455 Err(e) => return Err(de::Error::custom(e)),
456 }
457 }
458 Ok(out)
459}
460
461fn serialize_xmlns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
471where S: serde::Serializer {
472 if let Some(s) = os {
473 serializer.serialize_str(s)
474 } else {
475 serializer.serialize_str("urn:mpeg:dash:schema:mpd:2011")
476 }
477}
478
479fn serialize_xsi_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
480where S: serde::Serializer {
481 if let Some(s) = os {
482 serializer.serialize_str(s)
483 } else {
484 serializer.serialize_str("http://www.w3.org/2001/XMLSchema-instance")
485 }
486}
487
488fn serialize_cenc_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
489where S: serde::Serializer {
490 if let Some(s) = os {
491 serializer.serialize_str(s)
492 } else {
493 serializer.serialize_str("urn:mpeg:cenc:2013")
494 }
495}
496
497fn serialize_mspr_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
498where S: serde::Serializer {
499 if let Some(s) = os {
500 serializer.serialize_str(s)
501 } else {
502 serializer.serialize_str("urn:microsoft:playready")
503 }
504}
505
506fn serialize_xlink_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
507where S: serde::Serializer {
508 if let Some(s) = os {
509 serializer.serialize_str(s)
510 } else {
511 serializer.serialize_str("http://www.w3.org/1999/xlink")
512 }
513}
514
515fn serialize_dvb_ns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
516where S: serde::Serializer {
517 if let Some(s) = os {
518 serializer.serialize_str(s)
519 } else {
520 serializer.serialize_str("urn:dvb:dash-extensions:2014-1")
521 }
522}
523
524
525fn default_optstring_on_request() -> Option<String> {
529 Some("onRequest".to_string())
530}
531
532fn default_optstring_one() -> Option<String> {
533 Some(String::from("1"))
534}
535
536fn default_optstring_encoder() -> Option<String> {
537 Some(String::from("encoder"))
538}
539
540fn default_optbool_false() -> Option<bool> {
541 Some(false)
542}
543
544fn default_optu64_zero() -> Option<u64> {
545 Some(0)
546}
547
548
549#[skip_serializing_none]
562#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
563#[serde(default)]
564pub struct Title {
565 #[serde(rename = "$text")]
566 pub content: Option<String>,
567}
568
569#[skip_serializing_none]
571#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
572#[serde(default)]
573pub struct Source {
574 #[serde(rename = "$text")]
575 pub content: Option<String>,
576}
577
578#[skip_serializing_none]
580#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
581#[serde(default)]
582pub struct Copyright {
583 #[serde(rename = "$text")]
584 pub content: Option<String>,
585}
586
587#[skip_serializing_none]
589#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
590#[serde(default)]
591pub struct ProgramInformation {
592 #[serde(rename = "@lang")]
594 pub lang: Option<String>,
595 #[serde(rename = "@moreInformationURL")]
596 pub moreInformationURL: Option<String>,
597 pub Title: Option<Title>,
598 pub Source: Option<Source>,
599 pub Copyright: Option<Copyright>,
600 #[serde(rename(serialize = "scte214:ContentIdentifier", deserialize = "ContentIdentifier"))]
601 pub scte214ContentIdentifier: Option<Scte214ContentIdentifier>,
602}
603
604#[skip_serializing_none]
608#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
609#[serde(default)]
610pub struct Scte214ContentIdentifier {
611 #[serde(rename = "@type")]
612 pub idType: Option<String>,
613 #[serde(rename = "@value")]
614 pub idValue: Option<String>,
615}
616
617#[skip_serializing_none]
619#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
620#[serde(default)]
621pub struct S {
622 #[serde(rename = "@t")]
624 pub t: Option<u64>,
625 #[serde(rename = "@n")]
626 pub n: Option<u64>,
627 #[serde(rename = "@d")]
629 pub d: u64,
630 #[serde(rename = "@r")]
633 pub r: Option<i64>,
634 #[serde(rename = "@k")]
635 pub k: Option<u64>,
636}
637
638#[skip_serializing_none]
641#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
642#[serde(default)]
643pub struct SegmentTimeline {
644 #[serde(rename = "S")]
646 pub segments: Vec<S>,
647}
648
649#[skip_serializing_none]
656#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
657#[serde(default)]
658pub struct BitstreamSwitching {
659 #[serde(rename = "@sourceURL")]
660 pub source_url: Option<String>,
661 #[serde(rename = "@range")]
662 pub range: Option<String>,
663}
664
665#[skip_serializing_none]
669#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
670#[serde(default)]
671pub struct Initialization {
672 #[serde(rename = "@sourceURL")]
673 pub sourceURL: Option<String>,
674 #[serde(rename = "@range")]
675 pub range: Option<String>,
676}
677
678#[skip_serializing_none]
679#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
680#[serde(default)]
681pub struct RepresentationIndex {
682 #[serde(rename = "@range")]
683 pub range: Option<String>,
684 #[serde(rename = "@sourceURL")]
685 pub sourceURL: Option<String>,
686}
687
688#[skip_serializing_none]
691#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
692#[serde(default)]
693pub struct SegmentTemplate {
694 #[serde(rename = "@media")]
695 pub media: Option<String>,
696 #[serde(rename = "@index")]
697 pub index: Option<String>,
698 #[serde(rename = "@initialization")]
699 pub initialization: Option<String>,
700 #[serde(rename = "@bitstreamSwitching")]
701 pub bitstreamSwitching: Option<String>,
702 #[serde(rename = "@indexRange")]
703 pub indexRange: Option<String>,
704 #[serde(rename = "@indexRangeExact")]
705 pub indexRangeExact: Option<bool>,
706 #[serde(rename = "@startNumber")]
707 pub startNumber: Option<u64>,
708 #[serde(rename = "@duration")]
712 pub duration: Option<f64>,
713 #[serde(rename = "@timescale")]
714 pub timescale: Option<u64>,
715 #[serde(rename = "@eptDelta")]
717 pub eptDelta: Option<i64>,
718 #[serde(rename = "@pdDelta")]
721 pub pbDelta: Option<i64>,
722 #[serde(rename = "@presentationTimeOffset")]
723 pub presentationTimeOffset: Option<u64>,
724 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
725 pub availabilityTimeOffset: Option<f64>,
726 #[serde(rename = "@availabilityTimeComplete")]
727 pub availabilityTimeComplete: Option<bool>,
728 pub Initialization: Option<Initialization>,
729 #[serde(rename = "RepresentationIndex")]
730 pub representation_index: Option<RepresentationIndex>,
731 #[serde(rename = "FailoverContent")]
735 pub failover_content: Option<FailoverContent>,
736 pub SegmentTimeline: Option<SegmentTimeline>,
737 pub BitstreamSwitching: Option<BitstreamSwitching>,
738}
739
740#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
744#[serde(default)]
745pub struct Location {
746 #[serde(rename = "$text")]
747 pub url: String,
748}
749
750#[skip_serializing_none]
756#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
757#[serde(default)]
758pub struct BaseURL {
759 #[serde(rename = "@serviceLocation")]
760 pub serviceLocation: Option<String>,
761 #[serde(rename = "@byteRange")]
762 pub byte_range: Option<String>,
763 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
766 pub availability_time_offset: Option<f64>,
767 #[serde(rename = "@availabilityTimeComplete")]
768 pub availability_time_complete: Option<bool>,
769 #[serde(rename = "@timeShiftBufferDepth",
770 serialize_with = "serialize_xs_duration",
771 deserialize_with = "deserialize_xs_duration",
772 default)]
773 pub timeShiftBufferDepth: Option<Duration>,
774 #[serde(rename = "@dvb:priority", alias = "@priority")]
776 pub priority: Option<u64>,
777 #[serde(rename = "@dvb:weight", alias = "@weight")]
781 pub weight: Option<i64>,
782 #[serde(rename = "$text")]
783 pub base: String,
784}
785
786#[skip_serializing_none]
792#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
793#[serde(default)]
794pub struct Fcs {
795 #[serde(rename = "@t")]
798 pub t: u64,
799
800 #[serde(rename = "@d")]
803 pub d: Option<u64>,
804}
805
806#[skip_serializing_none]
809#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
810#[serde(default)]
811pub struct FailoverContent {
812 #[serde(rename = "@valid")]
815 pub valid: Option<bool>,
816 #[serde(rename = "FCS")]
817 pub fcs_list: Vec<Fcs>,
818}
819
820#[skip_serializing_none]
822#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
823#[serde(default)]
824pub struct SegmentBase {
825 #[serde(rename = "@timescale")]
826 pub timescale: Option<u64>,
827 #[serde(rename = "@presentationTimeOffset")]
828 pub presentationTimeOffset: Option<u64>,
829 #[serde(rename = "@indexRange")]
830 pub indexRange: Option<String>,
831 #[serde(rename = "@indexRangeExact")]
832 pub indexRangeExact: Option<bool>,
833 #[serde(rename = "@availabilityTimeOffset", serialize_with="serialize_opt_xsd_double")]
834 pub availabilityTimeOffset: Option<f64>,
835 #[serde(rename = "@availabilityTimeComplete")]
836 pub availabilityTimeComplete: Option<bool>,
837 #[serde(rename = "@presentationDuration")]
838 pub presentationDuration: Option<u64>,
839 #[serde(rename = "@eptDelta")]
841 pub eptDelta: Option<i64>,
842 #[serde(rename = "@pdDelta")]
845 pub pbDelta: Option<i64>,
846 pub Initialization: Option<Initialization>,
847 #[serde(rename = "RepresentationIndex")]
848 pub representation_index: Option<RepresentationIndex>,
849 #[serde(rename = "FailoverContent")]
850 pub failover_content: Option<FailoverContent>,
851}
852
853#[skip_serializing_none]
855#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
856#[serde(default)]
857pub struct SegmentURL {
858 #[serde(rename = "@media")]
859 pub media: Option<String>, #[serde(rename = "@mediaRange")]
861 pub mediaRange: Option<String>,
862 #[serde(rename = "@index")]
863 pub index: Option<String>, #[serde(rename = "@indexRange")]
865 pub indexRange: Option<String>,
866}
867
868#[skip_serializing_none]
870#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
871#[serde(default)]
872pub struct SegmentList {
873 #[serde(rename = "@duration")]
875 pub duration: Option<u64>,
876 #[serde(rename = "@timescale")]
877 pub timescale: Option<u64>,
878 #[serde(rename = "@indexRange")]
879 pub indexRange: Option<String>,
880 #[serde(rename = "@indexRangeExact")]
881 pub indexRangeExact: Option<bool>,
882 #[serde(rename = "@xlink:href", alias = "@href")]
884 pub href: Option<String>,
885 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
886 pub actuate: Option<String>,
887 #[serde(rename = "@xlink:type", alias = "@type")]
888 pub sltype: Option<String>,
889 #[serde(rename = "@xlink:show", alias = "@show")]
890 pub show: Option<String>,
891 pub Initialization: Option<Initialization>,
892 pub SegmentTimeline: Option<SegmentTimeline>,
893 pub BitstreamSwitching: Option<BitstreamSwitching>,
894 #[serde(rename = "SegmentURL")]
895 pub segment_urls: Vec<SegmentURL>,
896}
897
898#[skip_serializing_none]
899#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
900#[serde(default)]
901pub struct Resync {
902 #[serde(rename = "@type")]
903 pub rtype: Option<String>,
904 #[serde(rename = "@dT")]
905 pub dT: Option<u64>,
906 #[serde(rename = "@dImax")]
907 pub dImax: Option<f64>,
908 #[serde(rename = "@dImin")]
909 pub dImin: Option<f64>,
910 #[serde(rename = "@marker")]
911 pub marker: Option<bool>,
912}
913
914#[skip_serializing_none]
916#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
917#[serde(default)]
918pub struct AudioChannelConfiguration {
919 #[serde(rename = "@schemeIdUri")]
920 pub schemeIdUri: String,
921 #[serde(rename = "@value")]
922 pub value: Option<String>,
923 #[serde(rename = "@id")]
924 pub id: Option<String>,
925}
926
927#[skip_serializing_none]
929#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
930#[serde(default)]
931pub struct Language {
932 #[serde(rename = "$text")]
933 pub content: Option<String>,
934}
935
936#[skip_serializing_none]
942#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
943#[serde(default)]
944pub struct Preselection {
945 #[serde(rename = "@id", default = "default_optstring_one")]
946 pub id: Option<String>,
947 #[serde(rename = "@preselectionComponents")]
950 pub preselectionComponents: String,
951 #[serde(rename = "@lang")]
952 pub lang: Option<String>,
953 #[serde(rename = "@audioSamplingRate")]
954 pub audioSamplingRate: Option<String>,
955 #[serde(rename = "@codecs")]
957 pub codecs: String,
958 #[serde(rename = "@selectionPriority")]
959 pub selectionPriority: Option<u64>,
960 #[serde(rename = "@tag")]
961 pub tag: String,
962 pub FramePacking: Vec<FramePacking>,
963 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
964 pub ContentProtection: Vec<ContentProtection>,
965 pub OutputProtection: Option<OutputProtection>,
966 #[serde(rename = "EssentialProperty")]
967 pub essential_property: Vec<EssentialProperty>,
968 #[serde(rename = "SupplementalProperty")]
969 pub supplemental_property: Vec<SupplementalProperty>,
970 pub InbandEventStream: Vec<InbandEventStream>,
971 pub Switching: Vec<Switching>,
972 #[serde(rename = "GroupLabel")]
974 pub group_label: Vec<Label>,
975 pub Label: Vec<Label>,
976 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
977 pub Resync: Option<Resync>,
979 #[serde(rename = "Accessibility")]
980 pub accessibilities: Vec<Accessibility>,
981 #[serde(rename = "Role")]
982 pub roles: Vec<Role>,
983 #[serde(rename = "Rating")]
984 pub ratings: Vec<Rating>,
985 #[serde(rename = "Viewpoint")]
986 pub viewpoints: Vec<Viewpoint>,
987 #[serde(rename = "Language")]
989 pub languages: Vec<Language>,
990}
991
992#[skip_serializing_none]
995#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
996#[serde(default)]
997pub struct Rating {
998 #[serde(rename = "@id")]
999 pub id: Option<String>,
1000 #[serde(rename = "@schemeIdUri")]
1001 pub schemeIdUri: String,
1002 #[serde(rename = "@value")]
1003 pub value: Option<String>,
1004}
1005
1006#[skip_serializing_none]
1008#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1009#[serde(default)]
1010pub struct FramePacking {
1011 #[serde(rename = "@id")]
1012 pub id: Option<String>,
1013 #[serde(rename = "@schemeIdUri")]
1014 pub schemeIdUri: String,
1015 #[serde(rename = "@value")]
1016 pub value: Option<String>,
1017}
1018
1019#[skip_serializing_none]
1024#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1025#[serde(default)]
1026pub struct Switching {
1027 #[serde(rename = "@interval")]
1028 pub interval: Option<u64>,
1029 #[serde(rename = "@type")]
1031 pub stype: Option<String>,
1032}
1033
1034#[skip_serializing_none]
1036#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1037#[serde(default)]
1038pub struct Accessibility {
1039 #[serde(rename = "@schemeIdUri")]
1040 pub schemeIdUri: String,
1041 #[serde(rename = "@value")]
1042 pub value: Option<String>,
1043 #[serde(rename = "@id")]
1044 pub id: Option<String>,
1045}
1046
1047#[skip_serializing_none]
1049#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1050#[serde(default)]
1051pub struct Scope {
1052 #[serde(rename = "@schemeIdUri")]
1053 pub schemeIdUri: String,
1054 #[serde(rename = "@value")]
1055 pub value: Option<String>,
1056 #[serde(rename = "@id")]
1057 pub id: Option<String>,
1058}
1059
1060#[skip_serializing_none]
1062#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1063#[serde(default)]
1064pub struct SubRepresentation {
1065 #[serde(rename = "@level")]
1066 pub level: Option<u32>,
1067 #[serde(rename = "@dependencyLevel")]
1068 pub dependencyLevel: Option<String>,
1069 #[serde(rename = "@contentComponent")]
1071 pub contentComponent: Option<String>,
1072 #[serde(rename = "@mimeType")]
1073 pub mimeType: Option<String>,
1074 #[serde(rename = "@codecs")]
1076 pub codecs: Option<String>,
1077 #[serde(rename = "@contentType")]
1078 pub contentType: Option<String>,
1079 #[serde(rename = "@profiles")]
1080 pub profiles: Option<String>,
1081 #[serde(rename = "@segmentProfiles")]
1082 pub segmentProfiles: Option<String>,
1085 #[serde(rename = "@scanType")]
1087 pub scanType: Option<String>,
1088 #[serde(rename = "@frameRate")]
1089 pub frameRate: Option<String>, #[serde(rename = "@sar")]
1092 pub sar: Option<String>,
1093 #[serde(rename = "@bandwidth")]
1095 pub bandwidth: Option<u64>,
1096 #[serde(rename = "@audioSamplingRate")]
1097 pub audioSamplingRate: Option<String>,
1098 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1100 pub maxPlayoutRate: Option<f64>,
1101 #[serde(rename = "@codingDependency")]
1102 pub codingDependency: Option<bool>,
1103 #[serde(rename = "@width")]
1104 pub width: Option<u64>,
1105 #[serde(rename = "@height")]
1106 pub height: Option<u64>,
1107 #[serde(rename = "@startWithSAP")]
1108 pub startWithSAP: Option<u64>,
1109 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1110 pub maximumSAPPeriod: Option<f64>,
1111 pub FramePacking: Vec<FramePacking>,
1112 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1113 pub ContentProtection: Vec<ContentProtection>,
1114 pub OutputProtection: Option<OutputProtection>,
1115 #[serde(rename = "EssentialProperty")]
1116 pub essential_property: Vec<EssentialProperty>,
1117 #[serde(rename = "SupplementalProperty")]
1118 pub supplemental_property: Vec<SupplementalProperty>,
1119 pub InbandEventStream: Vec<InbandEventStream>,
1120 pub Switching: Vec<Switching>,
1121 #[serde(rename = "GroupLabel")]
1123 pub group_label: Vec<Label>,
1124 pub Label: Vec<Label>,
1125 pub ProducerReferenceTime: Option<ProducerReferenceTime>,
1126 pub Resync: Option<Resync>,
1128}
1129
1130#[skip_serializing_none]
1135#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1136#[serde(default)]
1137pub struct Representation {
1138 #[serde(rename = "@id")]
1140 pub id: Option<String>,
1141 #[serde(rename = "@bandwidth")]
1143 pub bandwidth: Option<u64>,
1144 #[serde(rename = "@qualityRanking")]
1148 pub qualityRanking: Option<u8>,
1149 #[serde(rename = "@dependencyId")]
1153 pub dependencyId: Option<String>,
1154 #[serde(rename = "@associationId")]
1155 pub associationId: Option<String>,
1156 #[serde(rename = "@associationType")]
1157 pub associationType: Option<String>,
1158 #[serde(rename = "@mediaStreamStructureId")]
1159 pub mediaStreamStructureId: Option<String>,
1160 #[serde(rename = "@profiles")]
1161 pub profiles: Option<String>,
1162 #[serde(rename = "@width")]
1163 pub width: Option<u64>,
1164 #[serde(rename = "@height")]
1165 pub height: Option<u64>,
1166 #[serde(rename = "@sar")]
1168 pub sar: Option<String>,
1169 #[serde(rename = "@frameRate")]
1170 pub frameRate: Option<String>, #[serde(rename = "@audioSamplingRate")]
1172 pub audioSamplingRate: Option<String>,
1173 #[serde(rename = "@mimeType")]
1176 pub mimeType: Option<String>,
1177 #[serde(rename = "@segmentProfiles")]
1180 pub segmentProfiles: Option<String>,
1181 #[serde(rename = "@codecs")]
1184 pub codecs: Option<String>,
1185 #[serde(rename = "@containerProfiles")]
1186 pub containerProfiles: Option<String>,
1187 #[serde(rename = "@maximumSAPPeriod")]
1188 pub maximumSAPPeriod: Option<f64>,
1189 #[serde(rename = "@startWithSAP")]
1190 pub startWithSAP: Option<u64>,
1191 #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1193 pub maxPlayoutRate: Option<f64>,
1194 #[serde(rename = "@codingDependency")]
1195 pub codingDependency: Option<bool>,
1196 #[serde(rename = "@scanType")]
1198 pub scanType: Option<String>,
1199 #[serde(rename = "@selectionPriority")]
1200 pub selectionPriority: Option<u64>,
1201 #[serde(rename = "@tag")]
1202 pub tag: Option<String>,
1203 #[serde(rename = "@contentType")]
1204 pub contentType: Option<String>,
1205 #[serde(rename = "@lang")]
1207 pub lang: Option<String>,
1208 #[serde(rename = "@sampleRate")]
1209 pub sampleRate: Option<u64>,
1210 #[serde(rename = "@numChannels")]
1211 pub numChannels: Option<u32>,
1212 #[serde(rename = "@xlink:href", alias = "@href")]
1213 pub href: Option<String>,
1214 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1215 pub actuate: Option<String>,
1216 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1217 pub scte214_supplemental_profiles: Option<String>,
1218 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1219 pub scte214_supplemental_codecs: Option<String>,
1220 pub FramePacking: Vec<FramePacking>,
1221 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1222 pub ContentProtection: Vec<ContentProtection>,
1223 pub OutputProtection: Option<OutputProtection>,
1224 #[serde(rename = "EssentialProperty")]
1225 pub essential_property: Vec<EssentialProperty>,
1226 #[serde(rename = "SupplementalProperty")]
1227 pub supplemental_property: Vec<SupplementalProperty>,
1228 pub InbandEventStream: Vec<InbandEventStream>,
1229 pub Switching: Vec<Switching>,
1230 #[serde(rename = "GroupLabel")]
1232 pub group_label: Vec<Label>,
1233 pub Label: Vec<Label>,
1234 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1235 pub Resync: Vec<Resync>,
1237 pub BaseURL: Vec<BaseURL>,
1238 pub SubRepresentation: Vec<SubRepresentation>,
1240 pub SegmentBase: Option<SegmentBase>,
1241 pub SegmentList: Option<SegmentList>,
1242 pub SegmentTemplate: Option<SegmentTemplate>,
1243 #[serde(rename = "RepresentationIndex")]
1244 pub representation_index: Option<RepresentationIndex>,
1245}
1246
1247#[skip_serializing_none]
1249#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1250#[serde(default)]
1251pub struct ContentComponent {
1252 #[serde(rename = "@id")]
1253 pub id: Option<String>,
1254 #[serde(rename = "@lang")]
1256 pub lang: Option<String>,
1257 #[serde(rename = "@contentType")]
1258 pub contentType: Option<String>,
1259 #[serde(rename = "@par")]
1260 pub par: Option<String>,
1261 #[serde(rename = "@tag")]
1262 pub tag: Option<String>,
1263 pub Accessibility: Vec<Accessibility>,
1264 pub Role: Vec<Role>,
1265 pub Rating: Vec<Rating>,
1266 pub Viewpoint: Vec<Viewpoint>,
1267}
1268
1269#[skip_serializing_none]
1271#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1272#[serde(default)]
1273pub struct CencPssh {
1274 #[serde(rename = "$text")]
1275 pub content: Option<String>,
1276}
1277
1278#[skip_serializing_none]
1280#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1281#[serde(default)]
1282pub struct Laurl {
1283 #[serde(rename = "@Lic_type")]
1284 pub lic_type: Option<String>,
1285 #[serde(rename = "$text")]
1286 pub content: Option<String>,
1287}
1288
1289#[skip_serializing_none]
1291#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1292#[serde(default)]
1293pub struct MsprPro {
1294 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
1295 pub xmlns: Option<String>,
1296 #[serde(rename = "$text")]
1297 pub content: Option<String>,
1298}
1299
1300#[skip_serializing_none]
1301#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1302#[serde(default)]
1303pub struct MsprIsEncrypted {
1304 #[serde(rename = "$text")]
1305 pub content: Option<String>,
1306}
1307
1308#[skip_serializing_none]
1309#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1310#[serde(default)]
1311pub struct MsprIVSize {
1312 #[serde(rename = "$text")]
1313 pub content: Option<String>,
1314}
1315
1316#[skip_serializing_none]
1317#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1318#[serde(default)]
1319pub struct MsprKid {
1320 #[serde(rename = "$text")]
1321 pub content: Option<String>,
1322}
1323
1324#[skip_serializing_none]
1325#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1326#[serde(default)]
1327pub struct OutputProtection {
1328 #[serde(rename = "@schemeIdUri")]
1329 pub schemeIdUri: String,
1330 #[serde(rename = "@value")]
1331 pub value: Option<String>,
1332 #[serde(rename = "@id")]
1333 pub id: Option<String>,
1334}
1335
1336#[skip_serializing_none]
1341#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1342#[serde(default)]
1343pub struct ContentProtection {
1344 #[serde(rename = "@robustness")]
1346 pub robustness: Option<String>,
1347 #[serde(rename = "@refId")]
1348 pub refId: Option<String>,
1349 #[serde(rename = "@ref")]
1351 pub r#ref: Option<String>,
1352 #[serde(rename = "@schemeIdUri")]
1354 pub schemeIdUri: String,
1355 #[serde(rename = "@value")]
1356 pub value: Option<String>,
1357 #[serde(rename = "@id")]
1358 pub id: Option<String>,
1359 #[serde(rename="cenc:pssh", alias="pssh")]
1361 pub cenc_pssh: Vec<CencPssh>,
1362 #[serde(rename = "@cenc:default_KID", alias = "@default_KID")]
1364 pub default_KID: Option<String>,
1365 #[serde(rename = "dashif:laurl", alias = "laurl")]
1367 pub laurl: Option<Laurl>,
1368 #[serde(rename = "clearkey:Laurl", alias = "Laurl")]
1372 pub clearkey_laurl: Option<Laurl>,
1373 #[serde(rename = "mspr:pro", alias = "pro")]
1375 pub msprpro: Option<MsprPro>,
1376 #[serde(rename = "mspr:IsEncrypted", alias = "IsEncrypted")]
1377 pub mspr_is_encrypted: Option<MsprIsEncrypted>,
1378 #[serde(rename = "mspr:IV_Size", alias = "IV_Size")]
1379 pub mspr_iv_size: Option<MsprIVSize>,
1380 #[serde(rename = "mspr:kid", alias = "kid")]
1381 pub mspr_kid: Option<MsprKid>,
1382}
1383
1384#[skip_serializing_none]
1390#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1391#[serde(default)]
1392pub struct Role {
1393 #[serde(rename = "@id")]
1394 pub id: Option<String>,
1395 #[serde(rename = "@schemeIdUri")]
1396 pub schemeIdUri: String,
1397 #[serde(rename = "@value")]
1398 pub value: Option<String>,
1399}
1400
1401#[skip_serializing_none]
1402#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1403#[serde(default)]
1404pub struct Viewpoint {
1405 #[serde(rename = "@id")]
1406 pub id: Option<String>,
1407 #[serde(rename = "@schemeIdUri")]
1408 pub schemeIdUri: String,
1409 #[serde(rename = "@value")]
1410 pub value: Option<String>,
1411}
1412
1413#[skip_serializing_none]
1414#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1415#[serde(default)]
1416pub struct Selection {
1417 #[serde(rename = "@dataEncoding")]
1418 pub dataEncoding: Option<String>,
1419 #[serde(rename = "@parameter")]
1420 pub parameter: Option<String>,
1421 #[serde(rename = "@data")]
1422 pub data: Option<String>,
1423}
1424
1425#[skip_serializing_none]
1426#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1427#[serde(default)]
1428pub struct SelectionInfo {
1429 #[serde(rename = "@selectionInfo")]
1430 pub selectionInfo: Option<String>,
1431 #[serde(rename = "@contactURL")]
1432 pub contactURL: Option<String>,
1433 pub Selection: Vec<Selection>,
1434}
1435
1436#[skip_serializing_none]
1443#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1444#[serde(default)]
1445pub struct Event {
1446 #[serde(rename = "@id")]
1447 pub id: Option<String>,
1448 #[serde(rename = "@presentationTime", default = "default_optu64_zero")]
1449 pub presentationTime: Option<u64>,
1450 #[serde(rename = "@presentationTimeOffset")]
1451 pub presentationTimeOffset: Option<u64>,
1452 #[serde(rename = "@duration")]
1453 pub duration: Option<u64>,
1454 #[serde(rename = "@timescale")]
1455 pub timescale: Option<u64>,
1456 #[serde(rename = "@contentEncoding")]
1459 pub contentEncoding: Option<String>,
1460 #[serde(rename = "@messageData")]
1463 pub messageData: Option<String>,
1464 pub SelectionInfo: Option<SelectionInfo>,
1465 #[cfg(feature = "scte35")]
1466 #[serde(rename = "scte35:Signal", alias="Signal")]
1467 #[cfg(feature = "scte35")]
1468 pub signal: Vec<Signal>,
1469 #[cfg(feature = "scte35")]
1470 #[serde(rename = "scte35:SpliceInfoSection", alias="SpliceInfoSection")]
1471 #[cfg(feature = "scte35")]
1472 pub splice_info_section: Vec<SpliceInfoSection>,
1473 #[serde(rename = "@value")]
1476 pub value: Option<String>,
1477 #[serde(rename = "$text")]
1480 pub content: Option<String>,
1481}
1482
1483#[skip_serializing_none]
1484#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1485#[serde(default)]
1486pub struct EventStream {
1487 #[serde(rename = "@xlink:href")]
1488 #[serde(alias = "@href")]
1489 pub href: Option<String>,
1490 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1491 pub actuate: Option<String>,
1492 #[serde(rename = "@messageData")]
1493 pub messageData: Option<String>,
1495 #[serde(rename = "@schemeIdUri")]
1496 pub schemeIdUri: String,
1497 #[serde(rename = "@value")]
1498 pub value: Option<String>,
1499 #[serde(rename = "@timescale")]
1500 pub timescale: Option<u64>,
1501 #[serde(rename = "@presentationTimeOffset")]
1502 pub presentationTimeOffset: Option<u64>,
1503 #[serde(rename = "Event")]
1504 pub event: Vec<Event>,
1505}
1506
1507#[skip_serializing_none]
1513#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1514#[serde(default)]
1515pub struct InbandEventStream {
1516 #[serde(rename = "@timescale")]
1517 pub timescale: Option<u64>,
1518 #[serde(rename = "@schemeIdUri")]
1519 pub schemeIdUri: String,
1520 #[serde(rename = "Event")]
1521 pub event: Vec<Event>,
1522 #[serde(rename = "@value")]
1523 pub value: Option<String>,
1524 #[serde(rename = "@xlink:href")]
1526 #[serde(alias = "@href")]
1527 pub href: Option<String>,
1528 #[serde(rename = "@xlink:actuate", alias = "@actuate")]
1529 pub actuate: Option<String>,
1530}
1531
1532#[skip_serializing_none]
1533#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1534#[serde(default)]
1535pub struct EssentialProperty {
1536 #[serde(rename = "@id")]
1537 pub id: Option<String>,
1538 #[serde(rename = "@schemeIdUri")]
1539 pub schemeIdUri: String,
1540 #[serde(rename = "@value")]
1541 pub value: Option<String>,
1542}
1543
1544#[skip_serializing_none]
1545#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1546#[serde(default)]
1547pub struct SupplementalProperty {
1548 #[serde(rename = "@id")]
1549 pub id: Option<String>,
1550 #[serde(rename = "@schemeIdUri")]
1551 pub schemeIdUri: String,
1552 #[serde(rename = "@value")]
1553 pub value: Option<String>,
1554 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1555 #[serde(rename(deserialize = "ContentIdentifier"))]
1556 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1557}
1558
1559#[skip_serializing_none]
1562#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1563#[serde(default)]
1564pub struct Label {
1565 #[serde(rename = "@id")]
1566 pub id: Option<String>,
1567 #[serde(rename = "@lang")]
1568 pub lang: Option<String>,
1569 #[serde(rename = "$text")]
1570 pub content: String,
1571}
1572
1573#[skip_serializing_none]
1581#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1582#[serde(default)]
1583pub struct AdaptationSet {
1584 #[serde(rename = "@id")]
1585 pub id: Option<String>,
1586 #[serde(rename = "@xlink:href", alias = "@href")]
1588 pub href: Option<String>,
1589 #[serde(rename = "@xlink:actuate", alias = "@actuate", default = "default_optstring_on_request")]
1590 pub actuate: Option<String>,
1591 #[serde(rename = "@group")]
1592 pub group: Option<i64>,
1593 #[serde(rename = "@selectionPriority")]
1594 pub selectionPriority: Option<u64>,
1595 #[serde(rename = "@contentType")]
1597 pub contentType: Option<String>,
1598 #[serde(rename = "@profiles")]
1599 pub profiles: Option<String>,
1600 #[serde(rename = "@lang")]
1602 pub lang: Option<String>,
1603 #[serde(rename = "@sar")]
1605 pub sar: Option<String>,
1606 #[serde(rename = "@par")]
1608 pub par: Option<String>,
1609 #[serde(rename = "@scanType")]
1611 pub scanType: Option<String>,
1612 #[serde(rename = "@segmentAlignment")]
1613 pub segmentAlignment: Option<bool>,
1614 #[serde(rename = "@segmentProfiles")]
1615 pub segmentProfiles: Option<String>,
1618 #[serde(rename = "@subsegmentAlignment")]
1619 pub subsegmentAlignment: Option<bool>,
1620 #[serde(rename = "@subsegmentStartsWithSAP")]
1621 pub subsegmentStartsWithSAP: Option<u64>,
1622 #[serde(rename = "@bitstreamSwitching")]
1623 pub bitstreamSwitching: Option<bool>,
1624 #[serde(rename = "@audioSamplingRate")]
1625 pub audioSamplingRate: Option<String>,
1626 #[serde(rename = "@width")]
1627 pub width: Option<u64>,
1628 #[serde(rename = "@height")]
1629 pub height: Option<u64>,
1630 #[serde(rename = "@mimeType")]
1632 pub mimeType: Option<String>,
1633 #[serde(rename = "@codecs")]
1635 pub codecs: Option<String>,
1636 #[serde(rename = "@minBandwidth")]
1637 pub minBandwidth: Option<u64>,
1638 #[serde(rename = "@maxBandwidth")]
1639 pub maxBandwidth: Option<u64>,
1640 #[serde(rename = "@minWidth")]
1641 pub minWidth: Option<u64>,
1642 #[serde(rename = "@maxWidth")]
1643 pub maxWidth: Option<u64>,
1644 #[serde(rename = "@minHeight")]
1645 pub minHeight: Option<u64>,
1646 #[serde(rename = "@maxHeight")]
1647 pub maxHeight: Option<u64>,
1648 #[serde(rename = "@frameRate")]
1649 pub frameRate: Option<String>, #[serde(rename = "@minFrameRate")]
1651 pub minFrameRate: Option<String>, #[serde(rename = "@maxFrameRate")]
1653 pub maxFrameRate: Option<String>, #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1656 pub maxPlayoutRate: Option<f64>,
1657 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1658 pub maximumSAPPeriod: Option<f64>,
1659 #[serde(rename = "@startWithSAP")]
1660 pub startWithSAP: Option<u64>,
1661 #[serde(rename = "@codingDependency")]
1662 pub codingDependency: Option<bool>,
1663 pub FramePacking: Vec<FramePacking>,
1664 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1665 pub ContentProtection: Vec<ContentProtection>,
1666 #[serde(rename = "EssentialProperty")]
1668 pub essential_property: Vec<EssentialProperty>,
1669 #[serde(rename = "SupplementalProperty")]
1670 pub supplemental_property: Vec<SupplementalProperty>,
1671 pub InbandEventStream: Vec<InbandEventStream>,
1672 pub Switching: Vec<Switching>,
1673 pub GroupLabel: Vec<Label>,
1675 pub Label: Vec<Label>,
1676 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1677 pub Resync: Vec<Resync>,
1679 pub Accessibility: Vec<Accessibility>,
1680 pub Role: Vec<Role>,
1681 pub Rating: Vec<Rating>,
1682 pub Viewpoint: Vec<Viewpoint>,
1683 pub ContentComponent: Vec<ContentComponent>,
1684 pub BaseURL: Vec<BaseURL>,
1685 pub SegmentBase: Option<SegmentBase>,
1686 pub SegmentList: Option<SegmentList>,
1687 pub SegmentTemplate: Option<SegmentTemplate>,
1688 #[serde(rename = "Representation")]
1689 pub representations: Vec<Representation>,
1690 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1691 pub scte214_supplemental_profiles: Option<String>,
1692 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1693 pub scte214_supplemental_codecs: Option<String>,
1694}
1695
1696#[skip_serializing_none]
1701#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1702#[serde(default)]
1703pub struct AssetIdentifier {
1704 #[serde(rename = "@schemeIdUri")]
1705 pub schemeIdUri: String,
1706 #[serde(rename = "@value")]
1707 pub value: Option<String>,
1708 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1709 #[serde(rename(deserialize = "ContentIdentifier"))]
1710 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1711}
1712
1713#[skip_serializing_none]
1718#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1719#[serde(default)]
1720pub struct Subset {
1721 #[serde(rename = "@id")]
1722 pub id: Option<String>,
1723 #[serde(rename = "@contains",
1726 deserialize_with = "deserialize_xsd_uintvector",
1727 serialize_with = "serialize_xsd_uintvector",
1728 default)]
1729 pub contains: Vec<u64>,
1730}
1731
1732#[skip_serializing_none]
1735#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1736#[serde(default)]
1737pub struct Period {
1738 #[serde(rename = "@xlink:href", alias = "@href")]
1740 pub href: Option<String>,
1741
1742 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
1743 pub actuate: Option<String>,
1744
1745 #[serde(rename = "@id")]
1746 pub id: Option<String>,
1747
1748 #[serde(rename = "@start",
1750 serialize_with = "serialize_xs_duration",
1751 deserialize_with = "deserialize_xs_duration",
1752 default)]
1753 pub start: Option<Duration>,
1754
1755 #[serde(rename = "@duration",
1757 serialize_with = "serialize_xs_duration",
1758 deserialize_with = "deserialize_xs_duration",
1759 default)]
1760 pub duration: Option<Duration>,
1761
1762 #[serde(rename = "@bitstreamSwitching", default)]
1764 pub bitstreamSwitching: Option<bool>,
1765
1766 pub BaseURL: Vec<BaseURL>,
1767
1768 pub SegmentBase: Option<SegmentBase>,
1769
1770 pub SegmentList: Option<SegmentList>,
1771
1772 pub SegmentTemplate: Option<SegmentTemplate>,
1773
1774 #[serde(rename = "AssetIdentifier")]
1775 pub asset_identifier: Option<AssetIdentifier>,
1776
1777 #[serde(rename = "EventStream")]
1778 pub event_streams: Vec<EventStream>,
1779
1780 #[serde(rename = "ServiceDescription")]
1781 pub service_description: Vec<ServiceDescription>,
1782
1783 pub ContentProtection: Vec<ContentProtection>,
1784
1785 #[serde(rename = "AdaptationSet")]
1786 pub adaptations: Vec<AdaptationSet>,
1787
1788 #[serde(rename = "Subset")]
1789 pub subsets: Vec<Subset>,
1790
1791 #[serde(rename = "SupplementalProperty")]
1792 pub supplemental_property: Vec<SupplementalProperty>,
1793
1794 #[serde(rename = "EmptyAdaptationSet")]
1795 pub empty_adaptations: Vec<AdaptationSet>,
1796
1797 #[serde(rename = "GroupLabel")]
1798 pub group_label: Vec<Label>,
1799
1800 #[serde(rename = "Preselection")]
1801 pub pre_selections: Vec<Preselection>,
1802
1803 #[serde(rename = "EssentialProperty")]
1804 pub essential_property: Vec<EssentialProperty>,
1805}
1806
1807#[skip_serializing_none]
1808#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1809#[serde(default)]
1810pub struct Reporting {
1811 #[serde(rename = "@id")]
1812 pub id: Option<String>,
1813 #[serde(rename = "@schemeIdUri")]
1814 pub schemeIdUri: String,
1815 #[serde(rename = "@value")]
1816 pub value: Option<String>,
1817 #[serde(rename = "@dvb:reportingUrl", alias = "@reportingUrl")]
1818 pub reportingUrl: Option<String>,
1819 #[serde(rename = "@dvb:probability", alias = "@probability")]
1820 pub probability: Option<u64>,
1821}
1822
1823#[skip_serializing_none]
1824#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1825#[serde(default)]
1826pub struct Range {
1827 #[serde(rename = "@starttime",
1828 serialize_with = "serialize_xs_duration",
1829 deserialize_with = "deserialize_xs_duration",
1830 default)]
1831 pub starttime: Option<Duration>,
1832 #[serde(rename = "@duration",
1833 serialize_with = "serialize_xs_duration",
1834 deserialize_with = "deserialize_xs_duration",
1835 default)]
1836 pub duration: Option<Duration>,
1837}
1838
1839#[skip_serializing_none]
1840#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1841#[serde(default)]
1842pub struct Metrics {
1843 #[serde(rename = "@metrics")]
1844 pub metrics: String,
1845 pub Reporting: Vec<Reporting>,
1846 pub Range: Vec<Range>,
1847}
1848
1849#[skip_serializing_none]
1850#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1851#[serde(default)]
1852pub struct Latency {
1853 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1854 pub min: Option<f64>,
1855 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1856 pub max: Option<f64>,
1857 #[serde(rename = "@target", serialize_with="serialize_opt_xsd_double")]
1858 pub target: Option<f64>,
1859 #[serde(rename = "@referenceId")]
1860 pub referenceId: Option<String>,
1861}
1862
1863#[skip_serializing_none]
1864#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1865#[serde(default)]
1866pub struct PlaybackRate {
1867 #[serde(rename = "@min", serialize_with="serialize_xsd_double")]
1868 pub min: f64,
1869 #[serde(rename = "@max", serialize_with="serialize_xsd_double")]
1870 pub max: f64,
1871}
1872
1873#[skip_serializing_none]
1874#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1875#[serde(default)]
1876pub struct ServiceDescription {
1877 #[serde(rename = "@id")]
1878 pub id: Option<String>,
1879 pub Latency: Option<Latency>,
1880 pub PlaybackRate: Option<PlaybackRate>,
1881 #[serde(rename = "Scope")]
1882 pub scopes: Vec<Scope>,
1883}
1884
1885#[skip_serializing_none]
1887#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1888#[serde(default)]
1889pub struct UTCTiming {
1890 #[serde(rename = "@id")]
1891 pub id: Option<String>,
1892 #[serde(rename = "@schemeIdUri")]
1895 pub schemeIdUri: String,
1896 #[serde(rename = "@value")]
1897 pub value: Option<String>,
1898}
1899
1900#[skip_serializing_none]
1905#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1906#[serde(default)]
1907pub struct ProducerReferenceTime {
1908 #[serde(rename = "@id")]
1910 pub id: Option<String>,
1911 #[serde(rename = "@inband", default = "default_optbool_false")]
1912 pub inband: Option<bool>,
1913 #[serde(rename = "@presentationTime")]
1915 pub presentationTime: Option<u64>,
1916 #[serde(rename = "@type", default = "default_optstring_encoder")]
1917 pub prtType: Option<String>,
1918 #[serde(rename = "@wallClockTime",
1922 alias="@wallclockTime",
1923 deserialize_with = "deserialize_xs_datetime",
1924 default)]
1925 pub wallClockTime: Option<XsDatetime>,
1926 pub UTCTiming: Option<UTCTiming>,
1927}
1928
1929#[skip_serializing_none]
1930#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1931#[serde(default)]
1932pub struct LeapSecondInformation {
1933 #[serde(rename = "@availabilityStartLeapOffset")]
1934 pub availabilityStartLeapOffset: Option<i64>,
1935 #[serde(rename = "@nextAvailabilityStartLeapOffset")]
1936 pub nextAvailabilityStartLeapOffset: Option<i64>,
1937 #[serde(rename = "@nextLeapChangeTime",
1938 deserialize_with = "deserialize_xs_datetime",
1939 default)]
1940 pub nextLeapChangeTime: Option<XsDatetime>,
1941}
1942
1943#[skip_serializing_none]
1950#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1951#[serde(default)]
1952pub struct PatchLocation {
1953 #[serde(rename = "@ttl", serialize_with="serialize_opt_xsd_double")]
1954 pub ttl: Option<f64>,
1955 #[serde(rename = "$text")]
1956 pub content: String,
1957}
1958
1959#[skip_serializing_none]
1961#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1962#[serde(default)]
1963pub struct MPD {
1964 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
1965 pub xmlns: Option<String>,
1966 #[serde(rename = "@id")]
1967 pub id: Option<String>,
1968 #[serde(rename = "@profiles")]
1969 pub profiles: Option<String>,
1970 #[serde(rename = "@type")]
1973 pub mpdtype: Option<String>,
1974 #[serde(rename = "@availabilityStartTime",
1975 deserialize_with = "deserialize_xs_datetime",
1976 default)]
1977 pub availabilityStartTime: Option<XsDatetime>,
1978 #[serde(rename = "@availabilityEndTime",
1979 deserialize_with = "deserialize_xs_datetime",
1980 default)]
1981 pub availabilityEndTime: Option<XsDatetime>,
1982 #[serde(rename = "@publishTime",
1983 deserialize_with = "deserialize_xs_datetime",
1984 default)]
1985 pub publishTime: Option<XsDatetime>,
1986 #[serde(rename = "@mediaPresentationDuration",
1987 serialize_with = "serialize_xs_duration",
1988 deserialize_with = "deserialize_xs_duration",
1989 default)]
1990 pub mediaPresentationDuration: Option<Duration>,
1991 #[serde(rename = "@minimumUpdatePeriod",
1992 serialize_with = "serialize_xs_duration",
1993 deserialize_with = "deserialize_xs_duration",
1994 default)]
1995 pub minimumUpdatePeriod: Option<Duration>,
1996 #[serde(rename = "@minBufferTime",
1998 serialize_with = "serialize_xs_duration",
1999 deserialize_with = "deserialize_xs_duration",
2000 default)]
2001 pub minBufferTime: Option<Duration>,
2002 #[serde(rename = "@timeShiftBufferDepth",
2005 serialize_with = "serialize_xs_duration",
2006 deserialize_with = "deserialize_xs_duration",
2007 default)]
2008 pub timeShiftBufferDepth: Option<Duration>,
2009 #[serde(rename = "@suggestedPresentationDelay",
2011 serialize_with = "serialize_xs_duration",
2012 deserialize_with = "deserialize_xs_duration",
2013 default)]
2014 pub suggestedPresentationDelay: Option<Duration>,
2015 #[serde(rename = "@maxSegmentDuration",
2016 serialize_with = "serialize_xs_duration",
2017 deserialize_with = "deserialize_xs_duration",
2018 default)]
2019 pub maxSegmentDuration: Option<Duration>,
2020 #[serde(rename = "@maxSubsegmentDuration",
2021 serialize_with = "serialize_xs_duration",
2022 deserialize_with = "deserialize_xs_duration",
2023 default)]
2024 pub maxSubsegmentDuration: Option<Duration>,
2025 #[serialize_always]
2027 #[serde(rename="@xmlns:xsi", alias="@xsi", serialize_with="serialize_xsi_ns")]
2028 pub xsi: Option<String>,
2029 #[serde(alias = "@ext", rename = "@xmlns:ext")]
2030 pub ext: Option<String>,
2031 #[serialize_always]
2033 #[serde(rename="@xmlns:cenc", alias="@cenc", serialize_with="serialize_cenc_ns")]
2034 pub cenc: Option<String>,
2035 #[serialize_always]
2037 #[serde(rename="@xmlns:mspr", alias="@mspr", serialize_with="serialize_mspr_ns")]
2038 pub mspr: Option<String>,
2039 #[serialize_always]
2041 #[serde(rename="@xmlns:xlink", alias="@xlink", serialize_with="serialize_xlink_ns")]
2042 pub xlink: Option<String>,
2043 #[cfg(feature = "scte35")]
2046 #[serialize_always]
2047 #[serde(rename="@xmlns:scte35", alias="@scte35", serialize_with="scte35::serialize_scte35_ns")]
2048 pub scte35: Option<String>,
2049 #[serialize_always]
2052 #[serde(rename="@xmlns:dvb", alias="@dvb", serialize_with="serialize_dvb_ns")]
2053 pub dvb: Option<String>,
2054 #[serde(rename = "@xsi:schemaLocation", alias = "@schemaLocation")]
2055 pub schemaLocation: Option<String>,
2056 #[serde(alias = "@scte214", rename = "@xmlns:scte214")]
2058 pub scte214: Option<String>,
2059 pub ProgramInformation: Vec<ProgramInformation>,
2060 #[serde(rename = "BaseURL")]
2062 pub base_url: Vec<BaseURL>,
2063 #[serde(rename = "Location", default)]
2064 pub locations: Vec<Location>,
2065 pub PatchLocation: Vec<PatchLocation>,
2068 pub ServiceDescription: Vec<ServiceDescription>,
2069 pub ContentProtection: Vec<ContentProtection>,
2071 #[serde(rename = "Period", default)]
2072 pub periods: Vec<Period>,
2073 pub Metrics: Vec<Metrics>,
2074 #[serde(rename = "EssentialProperty")]
2075 pub essential_property: Vec<EssentialProperty>,
2076 #[serde(rename = "SupplementalProperty")]
2077 pub supplemental_property: Vec<SupplementalProperty>,
2078 pub UTCTiming: Vec<UTCTiming>,
2079 pub LeapSecondInformation: Option<LeapSecondInformation>,
2081}
2082
2083impl std::fmt::Display for MPD {
2084 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2085 write!(f, "{}", quick_xml::se::to_string(self).map_err(|_| std::fmt::Error)?)
2086 }
2087}
2088
2089pub fn parse(xml: &str) -> Result<MPD, DashMpdError> {
2091 #[cfg(feature = "warn_ignored_elements")]
2092 {
2093 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2094 let _: MPD = serde_ignored::deserialize(xd, |path| {
2095 warn!("Unused XML element in manifest: {path}");
2096 }).map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2097 }
2098 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2099 let mpd: MPD = serde_path_to_error::deserialize(xd)
2100 .map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2101 Ok(mpd)
2102}
2103
2104
2105fn is_audio_codec(name: &str) -> bool {
2107 name.starts_with("mp4a") ||
2108 name.starts_with("aac") ||
2109 name.starts_with("vorbis") ||
2110 name.starts_with("opus") ||
2111 name.starts_with("ogg") ||
2112 name.starts_with("webm") ||
2113 name.starts_with("flac") ||
2114 name.starts_with("mp3") ||
2115 name.starts_with("mpeg") ||
2116 name.starts_with("3gpp") ||
2117 name.starts_with("wav") ||
2118 name.starts_with("ec-3") ||
2119 name.starts_with("ac-4") ||
2120 name.starts_with("dtsc") ||
2121 name.starts_with("aptx") ||
2122 name.starts_with("aiff") ||
2123 name.starts_with("mha1") }
2125
2126
2127pub fn is_audio_adaptation(a: &&AdaptationSet) -> bool {
2133 if let Some(codec) = &a.codecs {
2134 if is_audio_codec(codec) {
2135 return true;
2136 }
2137 }
2138 if let Some(ct) = &a.contentType {
2139 if ct == "audio" {
2140 return true;
2141 }
2142 }
2143 if let Some(mimetype) = &a.mimeType {
2144 if mimetype.starts_with("audio/") {
2145 return true;
2146 }
2147 }
2148 for r in a.representations.iter() {
2149 if let Some(ct) = &r.contentType {
2150 if ct == "audio" {
2151 return true;
2152 }
2153 }
2154 if let Some(mimetype) = &r.mimeType {
2155 if mimetype.starts_with("audio/") {
2156 return true;
2157 }
2158 }
2159 }
2160 false
2161}
2162
2163pub fn is_video_adaptation(a: &&AdaptationSet) -> bool {
2172 if is_audio_adaptation(a) {
2173 return false;
2174 }
2175 if let Some(ct) = &a.contentType {
2176 if ct == "video" {
2177 return true;
2178 }
2179 }
2180 if let Some(mimetype) = &a.mimeType {
2181 if mimetype.starts_with("video/") {
2182 return true;
2183 }
2184 }
2185 for r in a.representations.iter() {
2186 if let Some(ct) = &r.contentType {
2187 if ct == "video" {
2188 return true;
2189 }
2190 }
2191 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2194 return false;
2195 }
2196 if let Some(mimetype) = &r.mimeType {
2197 if mimetype.starts_with("video/") {
2198 return true;
2199 }
2200 }
2201 }
2202 false
2203}
2204
2205
2206fn is_subtitle_mimetype(mt: &str) -> bool {
2207 mt.eq("text/vtt") ||
2208 mt.eq("application/ttml+xml") ||
2209 mt.eq("application/x-sami")
2210
2211 }
2214
2215fn is_subtitle_codec(c: &str) -> bool {
2216 c == "wvtt" ||
2217 c == "c608" ||
2218 c == "stpp" ||
2219 c == "tx3g" ||
2220 c.starts_with("stpp.")
2221}
2222
2223pub fn is_subtitle_adaptation(a: &&AdaptationSet) -> bool {
2235 if a.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2236 return true;
2237 }
2238 if a.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2239 return true;
2240 }
2241 if a.codecs.as_deref().is_some_and(is_subtitle_codec) {
2242 return true;
2243 }
2244 for cc in a.ContentComponent.iter() {
2245 if cc.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2246 return true;
2247 }
2248 }
2249 for r in a.representations.iter() {
2250 if r.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2251 return true;
2252 }
2253 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2255 return true;
2256 }
2257 }
2258 false
2259}
2260
2261
2262#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2264pub enum SubtitleType {
2265 Vtt,
2267 Srt,
2269 Sub,
2271 Ass,
2273 Ttxt,
2275 Ttml,
2277 Sami,
2279 Wvtt,
2283 Stpp,
2285 Eia608,
2287 Unknown,
2288}
2289
2290fn subtitle_type_for_mimetype(mt: &str) -> Option<SubtitleType> {
2291 match mt {
2292 "text/vtt" => Some(SubtitleType::Vtt),
2293 "application/ttml+xml" => Some(SubtitleType::Ttml),
2294 "application/x-sami" => Some(SubtitleType::Sami),
2295 _ => None
2296 }
2297}
2298
2299pub fn subtitle_type(a: &&AdaptationSet) -> SubtitleType {
2300 if let Some(mimetype) = &a.mimeType {
2301 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2302 return st;
2303 }
2304 }
2305 if let Some(codecs) = &a.codecs {
2306 if codecs == "wvtt" {
2307 return SubtitleType::Wvtt;
2309 }
2310 if codecs == "c608" {
2311 return SubtitleType::Eia608;
2312 }
2313 if codecs == "tx3g" {
2314 return SubtitleType::Ttxt;
2315 }
2316 if codecs == "stpp" {
2317 return SubtitleType::Stpp;
2318 }
2319 if codecs.starts_with("stpp.") {
2320 return SubtitleType::Stpp;
2321 }
2322 }
2323 for r in a.representations.iter() {
2324 if let Some(mimetype) = &r.mimeType {
2325 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2326 return st;
2327 }
2328 }
2329 if let Some(codecs) = &r.codecs {
2330 if codecs == "wvtt" {
2331 return SubtitleType::Wvtt;
2332 }
2333 if codecs == "c608" {
2334 return SubtitleType::Eia608;
2335 }
2336 if codecs == "tx3g" {
2337 return SubtitleType::Ttxt;
2338 }
2339 if codecs == "stpp" {
2340 return SubtitleType::Stpp;
2341 }
2342 if codecs.starts_with("stpp.") {
2343 return SubtitleType::Stpp;
2344 }
2345 }
2346 }
2347 SubtitleType::Unknown
2348}
2349
2350
2351#[allow(dead_code)]
2352fn content_protection_type(cp: &ContentProtection) -> String {
2353 if let Some(v) = &cp.value {
2354 if v.eq("cenc") {
2355 return String::from("cenc");
2356 }
2357 if v.eq("Widevine") {
2358 return String::from("Widevine");
2359 }
2360 if v.eq("MSPR 2.0") {
2361 return String::from("PlayReady");
2362 }
2363 }
2364 let uri = &cp.schemeIdUri;
2366 let uri = uri.to_lowercase();
2367 if uri.eq("urn:mpeg:dash:mp4protection:2011") {
2368 return String::from("cenc");
2369 }
2370 if uri.eq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed") {
2371 return String::from("Widevine");
2372 }
2373 if uri.eq("urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95") {
2374 return String::from("PlayReady");
2375 }
2376 if uri.eq("urn:uuid:94ce86fb-07ff-4f43-adb8-93d2fa968ca2") {
2377 return String::from("FairPlay");
2378 }
2379 if uri.eq("urn:uuid:3ea8778f-7742-4bf9-b18b-e834b2acbd47") {
2380 return String::from("Clear Key AES-128");
2381 }
2382 if uri.eq("urn:uuid:be58615b-19c4-4684-88b3-c8c57e99e957") {
2383 return String::from("Clear Key SAMPLE-AES");
2384 }
2385 if uri.eq("urn:uuid:adb41c24-2dbf-4a6d-958b-4457c0d27b95") {
2386 return String::from("Nagra");
2387 }
2388 if uri.eq("urn:uuid:5e629af5-38da-4063-8977-97ffbd9902d4") {
2389 return String::from("Marlin");
2390 }
2391 if uri.eq("urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb") {
2392 return String::from("Adobe PrimeTime");
2393 }
2394 if uri.eq("urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b") {
2395 return String::from("W3C Common PSSH box");
2396 }
2397 if uri.eq("urn:uuid:80a6be7e-1448-4c37-9e70-d5aebe04c8d2") {
2398 return String::from("Irdeto Content Protection");
2399 }
2400 if uri.eq("urn:uuid:3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c") {
2401 return String::from("WisePlay-ChinaDRM");
2402 }
2403 if uri.eq("urn:uuid:616c7469-6361-7374-2d50-726f74656374") {
2404 return String::from("Alticast");
2405 }
2406 if uri.eq("urn:uuid:6dd8b3c3-45f4-4a68-bf3a-64168d01a4a6") {
2407 return String::from("ABV DRM");
2408 }
2409 if uri.eq("urn:mpeg:dash:sea:2012") {
2411 return String::from("SEA");
2412 }
2413 String::from("<unknown>")
2414}
2415
2416
2417fn check_segment_template_duration(
2418 st: &SegmentTemplate,
2419 max_seg_duration: &Duration,
2420 outer_timescale: u64) -> Vec<String>
2421{
2422 let mut errors = Vec::new();
2423 if let Some(timeline) = &st.SegmentTimeline {
2424 for s in &timeline.segments {
2425 let sd = s.d / st.timescale.unwrap_or(outer_timescale);
2426 if sd > max_seg_duration.as_secs() {
2427 errors.push(String::from("SegmentTimeline has segment@d > @maxSegmentDuration"));
2428 }
2429 }
2430 }
2431 errors
2432}
2433
2434fn check_segment_template_conformity(st: &SegmentTemplate) -> Vec<String> {
2435 let mut errors = Vec::new();
2436 if let Some(md) = &st.media {
2437 if !valid_url_p(md) {
2438 errors.push(format!("invalid URL {md}"));
2439 }
2440 if md.contains("$Number$") && md.contains("$Time") {
2441 errors.push(String::from("both $Number$ and $Time$ are used in media template URL"));
2442 }
2443 }
2444 if let Some(init) = &st.initialization {
2445 if !valid_url_p(init) {
2446 errors.push(format!("invalid URL {init}"));
2447 }
2448 if init.contains("$Number") {
2449 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2450 }
2451 if init.contains("$Time") {
2452 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2453 }
2454 }
2455 if st.duration.is_some() && st.SegmentTimeline.is_some() {
2456 errors.push(String::from("both SegmentTemplate.duration and SegmentTemplate.SegmentTimeline present"));
2457 }
2458 errors
2459}
2460
2461
2462fn valid_url_p(u: &str) -> bool {
2465 use url::ParseError;
2466
2467 match Url::parse(u) {
2468 Ok(url) => {
2469 url.scheme() == "https" ||
2470 url.scheme() == "http" ||
2471 url.scheme() == "ftp" ||
2472 url.scheme() == "file" ||
2473 url.scheme() == "data"
2474 },
2475 Err(ParseError::RelativeUrlWithoutBase) => true,
2476 Err(_) => false,
2477 }
2478}
2479
2480pub fn check_conformity(mpd: &MPD) -> Vec<String> {
2482 let mut errors = Vec::new();
2483
2484 for p in &mpd.periods {
2487 if p.adaptations.is_empty() {
2488 errors.push(format!("Period with @id {} contains no AdaptationSet elements",
2489 p.id.clone().unwrap_or(String::from("<unspecified>"))));
2490 }
2491 for a in &p.adaptations {
2492 if let Some(mh) = a.maxHeight {
2493 if let Some(mr) = a.representations.iter().max_by_key(|r| r.height.unwrap_or(0)) {
2494 if mr.height.unwrap_or(0) > mh {
2495 errors.push(String::from("invalid @maxHeight on AdaptationSet"));
2496 }
2497 }
2498 }
2499 }
2500 }
2501 for p in &mpd.periods {
2504 for a in &p.adaptations {
2505 if let Some(mw) = a.maxWidth {
2506 if let Some(mr) = a.representations.iter().max_by_key(|r| r.width.unwrap_or(0)) {
2507 if mr.width.unwrap_or(0) > mw {
2508 errors.push(String::from("invalid @maxWidth on AdaptationSet"));
2509 }
2510 }
2511 }
2512 }
2513 }
2514 for p in &mpd.periods {
2517 for a in &p.adaptations {
2518 if let Some(mb) = a.maxBandwidth {
2519 if let Some(mr) = a.representations.iter().max_by_key(|r| r.bandwidth.unwrap_or(0)) {
2520 if mr.bandwidth.unwrap_or(0) > mb {
2521 errors.push(String::from("invalid @maxBandwidth on AdaptationSet"));
2522 }
2523 }
2524 }
2525 }
2526 }
2527 if let Some(max_seg_duration) = mpd.maxSegmentDuration {
2529 for p in &mpd.periods {
2530 for a in &p.adaptations {
2531 let mut outer_timescale = 1;
2542 if let Some(st) = &a.SegmentTemplate {
2543 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2544 .into_iter()
2545 .for_each(|msg| errors.push(msg));
2546 if let Some(ots) = st.timescale {
2547 outer_timescale = ots;
2548 }
2549 }
2550 for r in &a.representations {
2551 if let Some(st) = &r.SegmentTemplate {
2552 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2553 .into_iter()
2554 .for_each(|msg| errors.push(msg));
2555 }
2556 }
2557 }
2558 }
2559 }
2560
2561 for bu in &mpd.base_url {
2562 if !valid_url_p(&bu.base) {
2563 errors.push(format!("invalid URL {}", &bu.base));
2564 }
2565 }
2566 for p in &mpd.periods {
2567 for bu in &p.BaseURL {
2568 if !valid_url_p(&bu.base) {
2569 errors.push(format!("invalid URL {}", &bu.base));
2570 }
2571 }
2572 for a in &p.adaptations {
2573 for bu in &a.BaseURL {
2574 if !valid_url_p(&bu.base) {
2575 errors.push(format!("invalid URL {}", &bu.base));
2576 }
2577 }
2578 if let Some(st) = &a.SegmentTemplate {
2579 check_segment_template_conformity(st)
2580 .into_iter()
2581 .for_each(|msg| errors.push(msg));
2582 }
2583 for r in &a.representations {
2584 for bu in &r.BaseURL {
2585 if !valid_url_p(&bu.base) {
2586 errors.push(format!("invalid URL {}", &bu.base));
2587 }
2588 }
2589 if let Some(sb) = &r.SegmentBase {
2590 if let Some(init) = &sb.Initialization {
2591 if let Some(su) = &init.sourceURL {
2592 if !valid_url_p(su) {
2593 errors.push(format!("invalid URL {su}"));
2594 }
2595 if su.contains("$Number") {
2596 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2597 }
2598 if su.contains("$Time") {
2599 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2600 }
2601 }
2602 }
2603 if let Some(ri) = &sb.representation_index {
2604 if let Some(su) = &ri.sourceURL {
2605 if !valid_url_p(su) {
2606 errors.push(format!("invalid URL {su}"));
2607 }
2608 }
2609 }
2610 }
2611 if let Some(sl) = &r.SegmentList {
2612 if let Some(hr) = &sl.href {
2613 if !valid_url_p(hr) {
2614 errors.push(format!("invalid URL {hr}"));
2615 }
2616 }
2617 if let Some(init) = &sl.Initialization {
2618 if let Some(su) = &init.sourceURL {
2619 if !valid_url_p(su) {
2620 errors.push(format!("invalid URL {su}"));
2621 }
2622 if su.contains("$Number") {
2623 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2624 }
2625 if su.contains("$Time") {
2626 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2627 }
2628 }
2629 }
2630 for su in &sl.segment_urls {
2631 if let Some(md) = &su.media {
2632 if !valid_url_p(md) {
2633 errors.push(format!("invalid URL {md}"));
2634 }
2635 }
2636 if let Some(ix) = &su.index {
2637 if !valid_url_p(ix) {
2638 errors.push(format!("invalid URL {ix}"));
2639 }
2640 }
2641 }
2642 }
2643 if let Some(st) = &r.SegmentTemplate {
2644 check_segment_template_conformity(st)
2645 .into_iter()
2646 .for_each(|msg| errors.push(msg));
2647 }
2648 }
2649 }
2650 }
2651 for pi in &mpd.ProgramInformation {
2652 if let Some(u) = &pi.moreInformationURL {
2653 if !valid_url_p(u) {
2654 errors.push(format!("invalid URL {u}"));
2655 }
2656 }
2657 }
2658 errors
2659}
2660
2661#[cfg(test)]
2662mod tests {
2663 use proptest::prelude::*;
2664
2665 proptest! {
2666 #[test]
2667 fn doesnt_crash(s in "\\PC*") {
2668 let _ = super::parse_xs_duration(&s);
2669 let _ = super::parse_xs_datetime(&s);
2670 }
2671 }
2672
2673 #[test]
2674 fn test_parse_xs_duration() {
2675 use std::time::Duration;
2676 use super::parse_xs_duration;
2677
2678 assert!(parse_xs_duration("").is_err());
2679 assert!(parse_xs_duration("foobles").is_err());
2680 assert!(parse_xs_duration("P").is_err());
2681 assert!(parse_xs_duration("PW").is_err());
2682 assert!(parse_xs_duration("-PT4.5S").is_err());
2684 assert!(parse_xs_duration("1Y2M3DT4H5M6S").is_err()); assert_eq!(parse_xs_duration("PT3H11M53S").ok(), Some(Duration::new(11513, 0)));
2686 assert_eq!(parse_xs_duration("PT42M30S").ok(), Some(Duration::new(2550, 0)));
2687 assert_eq!(parse_xs_duration("PT30M38S").ok(), Some(Duration::new(1838, 0)));
2688 assert_eq!(parse_xs_duration("PT0H10M0.00S").ok(), Some(Duration::new(600, 0)));
2689 assert_eq!(parse_xs_duration("PT1.5S").ok(), Some(Duration::new(1, 500_000_000)));
2690 assert_eq!(parse_xs_duration("PT1.500S").ok(), Some(Duration::new(1, 500_000_000)));
2691 assert_eq!(parse_xs_duration("PT1.500000000S").ok(), Some(Duration::new(1, 500_000_000)));
2692 assert_eq!(parse_xs_duration("PT0S").ok(), Some(Duration::new(0, 0)));
2693 assert_eq!(parse_xs_duration("PT0.001S").ok(), Some(Duration::new(0, 1_000_000)));
2694 assert_eq!(parse_xs_duration("PT0.00100S").ok(), Some(Duration::new(0, 1_000_000)));
2695 assert_eq!(parse_xs_duration("PT344S").ok(), Some(Duration::new(344, 0)));
2696 assert_eq!(parse_xs_duration("PT634.566S").ok(), Some(Duration::new(634, 566_000_000)));
2697 assert_eq!(parse_xs_duration("PT72H").ok(), Some(Duration::new(72*60*60, 0)));
2698 assert_eq!(parse_xs_duration("PT0H0M30.030S").ok(), Some(Duration::new(30, 30_000_000)));
2699 assert_eq!(parse_xs_duration("PT1004199059S").ok(), Some(Duration::new(1004199059, 0)));
2700 assert_eq!(parse_xs_duration("P0Y20M0D").ok(), Some(Duration::new(51840000, 0)));
2701 assert_eq!(parse_xs_duration("PT1M30.5S").ok(), Some(Duration::new(90, 500_000_000)));
2702 assert_eq!(parse_xs_duration("PT10M10S").ok(), Some(Duration::new(610, 0)));
2703 assert_eq!(parse_xs_duration("PT1H0.040S").ok(), Some(Duration::new(3600, 40_000_000)));
2704 assert_eq!(parse_xs_duration("PT00H03M30SZ").ok(), Some(Duration::new(210, 0)));
2705 assert_eq!(parse_xs_duration("PT3.14159S").ok(), Some(Duration::new(3, 141_590_000)));
2706 assert_eq!(parse_xs_duration("PT3.14159265S").ok(), Some(Duration::new(3, 141_592_650)));
2707 assert_eq!(parse_xs_duration("PT3.141592653S").ok(), Some(Duration::new(3, 141_592_653)));
2708 assert_eq!(parse_xs_duration("PT3.141592653897S").ok(), Some(Duration::new(3, 141_592_653)));
2710 assert_eq!(parse_xs_duration("P0W").ok(), Some(Duration::new(0, 0)));
2711 assert_eq!(parse_xs_duration("P26W").ok(), Some(Duration::new(15724800, 0)));
2712 assert_eq!(parse_xs_duration("P52W").ok(), Some(Duration::new(31449600, 0)));
2713 assert_eq!(parse_xs_duration("P10D").ok(), Some(Duration::new(864000, 0)));
2714 assert_eq!(parse_xs_duration("P0Y").ok(), Some(Duration::new(0, 0)));
2715 assert_eq!(parse_xs_duration("P1Y").ok(), Some(Duration::new(31536000, 0)));
2716 assert_eq!(parse_xs_duration("P1Y0W0S").ok(), Some(Duration::new(31536000, 0)));
2717 assert_eq!(parse_xs_duration("PT4H").ok(), Some(Duration::new(14400, 0)));
2718 assert_eq!(parse_xs_duration("+PT4H").ok(), Some(Duration::new(14400, 0)));
2719 assert_eq!(parse_xs_duration("PT0004H").ok(), Some(Duration::new(14400, 0)));
2720 assert_eq!(parse_xs_duration("PT4H0M").ok(), Some(Duration::new(14400, 0)));
2721 assert_eq!(parse_xs_duration("PT4H0S").ok(), Some(Duration::new(14400, 0)));
2722 assert_eq!(parse_xs_duration("P23DT23H").ok(), Some(Duration::new(2070000, 0)));
2723 assert_eq!(parse_xs_duration("P0Y0M0DT0H4M20.880S").ok(), Some(Duration::new(260, 880_000_000)));
2724 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6.7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2725 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6,7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2726
2727 }
2731
2732 #[test]
2733 fn test_serialize_xs_duration() {
2734 use std::time::Duration;
2735 use super::MPD;
2736
2737 fn serialized_xs_duration(d: Duration) -> String {
2738 let mpd = MPD {
2739 minBufferTime: Some(d),
2740 ..Default::default()
2741 };
2742 let xml = mpd.to_string();
2743 let doc = roxmltree::Document::parse(&xml).unwrap();
2744 String::from(doc.root_element().attribute("minBufferTime").unwrap())
2745 }
2746
2747 assert_eq!("PT0S", serialized_xs_duration(Duration::new(0, 0)));
2748 assert_eq!("PT0.001S", serialized_xs_duration(Duration::new(0, 1_000_000)));
2749 assert_eq!("PT42S", serialized_xs_duration(Duration::new(42, 0)));
2750 assert_eq!("PT1.5S", serialized_xs_duration(Duration::new(1, 500_000_000)));
2751 assert_eq!("PT30.03S", serialized_xs_duration(Duration::new(30, 30_000_000)));
2752 assert_eq!("PT1M30.5S", serialized_xs_duration(Duration::new(90, 500_000_000)));
2753 assert_eq!("PT5M44S", serialized_xs_duration(Duration::new(344, 0)));
2754 assert_eq!("PT42M30S", serialized_xs_duration(Duration::new(2550, 0)));
2755 assert_eq!("PT30M38S", serialized_xs_duration(Duration::new(1838, 0)));
2756 assert_eq!("PT10M10S", serialized_xs_duration(Duration::new(610, 0)));
2757 assert_eq!("PT1H0M0.04S", serialized_xs_duration(Duration::new(3600, 40_000_000)));
2758 assert_eq!("PT3H11M53S", serialized_xs_duration(Duration::new(11513, 0)));
2759 assert_eq!("PT4H0M0S", serialized_xs_duration(Duration::new(14400, 0)));
2760 }
2761
2762 #[test]
2763 fn test_parse_xs_datetime() {
2764 use chrono::{DateTime, NaiveDate};
2765 use chrono::offset::Utc;
2766 use super::parse_xs_datetime;
2767
2768 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2769 .unwrap()
2770 .and_hms_opt(1, 3, 2)
2771 .unwrap();
2772 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02Z").ok(),
2773 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2774 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2775 .unwrap()
2776 .and_hms_nano_opt(1, 3, 2, 958*1000*1000)
2777 .unwrap();
2778 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02.958Z").ok(),
2779 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2780 }
2781}