1#![allow(non_snake_case)]
65
66#[cfg(feature = "fetch")]
71mod media;
72#[cfg(all(feature = "fetch", feature = "libav"))]
73mod libav;
74#[cfg(all(feature = "fetch", not(feature = "libav")))]
75mod 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 = "@maxFrameRate")]
1651 pub maxFrameRate: Option<String>, #[serde(rename = "@maxPlayoutRate", serialize_with="serialize_opt_xsd_double")]
1654 pub maxPlayoutRate: Option<f64>,
1655 #[serde(rename = "@maximumSAPPeriod", serialize_with="serialize_opt_xsd_double")]
1656 pub maximumSAPPeriod: Option<f64>,
1657 #[serde(rename = "@startWithSAP")]
1658 pub startWithSAP: Option<u64>,
1659 #[serde(rename = "@codingDependency")]
1660 pub codingDependency: Option<bool>,
1661 pub FramePacking: Vec<FramePacking>,
1662 pub AudioChannelConfiguration: Vec<AudioChannelConfiguration>,
1663 pub ContentProtection: Vec<ContentProtection>,
1664 #[serde(rename = "EssentialProperty")]
1666 pub essential_property: Vec<EssentialProperty>,
1667 #[serde(rename = "SupplementalProperty")]
1668 pub supplemental_property: Vec<SupplementalProperty>,
1669 pub InbandEventStream: Vec<InbandEventStream>,
1670 pub Switching: Vec<Switching>,
1671 pub GroupLabel: Vec<Label>,
1673 pub Label: Vec<Label>,
1674 pub ProducerReferenceTime: Vec<ProducerReferenceTime>,
1675 pub Resync: Vec<Resync>,
1677 pub Accessibility: Vec<Accessibility>,
1678 pub Role: Vec<Role>,
1679 pub Rating: Vec<Rating>,
1680 pub Viewpoint: Vec<Viewpoint>,
1681 pub ContentComponent: Vec<ContentComponent>,
1682 pub BaseURL: Vec<BaseURL>,
1683 pub SegmentBase: Option<SegmentBase>,
1684 pub SegmentList: Option<SegmentList>,
1685 pub SegmentTemplate: Option<SegmentTemplate>,
1686 #[serde(rename = "Representation")]
1687 pub representations: Vec<Representation>,
1688 #[serde(rename = "@scte214:supplementalProfiles", alias = "@supplementalProfiles")]
1689 pub scte214_supplemental_profiles: Option<String>,
1690 #[serde(rename = "@scte214:supplementalCodecs", alias = "@supplementalCodecs")]
1691 pub scte214_supplemental_codecs: Option<String>,
1692}
1693
1694#[skip_serializing_none]
1699#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1700#[serde(default)]
1701pub struct AssetIdentifier {
1702 #[serde(rename = "@schemeIdUri")]
1703 pub schemeIdUri: String,
1704 #[serde(rename = "@value")]
1705 pub value: Option<String>,
1706 #[serde(rename(serialize = "scte214:ContentIdentifier"))]
1707 #[serde(rename(deserialize = "ContentIdentifier"))]
1708 pub scte214ContentIdentifiers: Vec<Scte214ContentIdentifier>,
1709}
1710
1711#[skip_serializing_none]
1716#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1717#[serde(default)]
1718pub struct Subset {
1719 #[serde(rename = "@id")]
1720 pub id: Option<String>,
1721 #[serde(rename = "@contains",
1724 deserialize_with = "deserialize_xsd_uintvector",
1725 serialize_with = "serialize_xsd_uintvector",
1726 default)]
1727 pub contains: Vec<u64>,
1728}
1729
1730#[skip_serializing_none]
1733#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1734#[serde(default)]
1735pub struct Period {
1736 #[serde(rename = "@xlink:href", alias = "@href")]
1738 pub href: Option<String>,
1739
1740 #[serde(rename = "@xlink:actuate", alias = "@actuate", default="default_optstring_on_request")]
1741 pub actuate: Option<String>,
1742
1743 #[serde(rename = "@id")]
1744 pub id: Option<String>,
1745
1746 #[serde(rename = "@start",
1748 serialize_with = "serialize_xs_duration",
1749 deserialize_with = "deserialize_xs_duration",
1750 default)]
1751 pub start: Option<Duration>,
1752
1753 #[serde(rename = "@duration",
1755 serialize_with = "serialize_xs_duration",
1756 deserialize_with = "deserialize_xs_duration",
1757 default)]
1758 pub duration: Option<Duration>,
1759
1760 #[serde(rename = "@bitstreamSwitching", default)]
1762 pub bitstreamSwitching: Option<bool>,
1763
1764 pub BaseURL: Vec<BaseURL>,
1765
1766 pub SegmentBase: Option<SegmentBase>,
1767
1768 pub SegmentList: Option<SegmentList>,
1769
1770 pub SegmentTemplate: Option<SegmentTemplate>,
1771
1772 #[serde(rename = "AssetIdentifier")]
1773 pub asset_identifier: Option<AssetIdentifier>,
1774
1775 #[serde(rename = "EventStream")]
1776 pub event_streams: Vec<EventStream>,
1777
1778 #[serde(rename = "ServiceDescription")]
1779 pub service_description: Vec<ServiceDescription>,
1780
1781 pub ContentProtection: Vec<ContentProtection>,
1782
1783 #[serde(rename = "AdaptationSet")]
1784 pub adaptations: Vec<AdaptationSet>,
1785
1786 #[serde(rename = "Subset")]
1787 pub subsets: Vec<Subset>,
1788
1789 #[serde(rename = "SupplementalProperty")]
1790 pub supplemental_property: Vec<SupplementalProperty>,
1791
1792 #[serde(rename = "EmptyAdaptationSet")]
1793 pub empty_adaptations: Vec<AdaptationSet>,
1794
1795 #[serde(rename = "GroupLabel")]
1796 pub group_label: Vec<Label>,
1797
1798 #[serde(rename = "Preselection")]
1799 pub pre_selections: Vec<Preselection>,
1800
1801 #[serde(rename = "EssentialProperty")]
1802 pub essential_property: Vec<EssentialProperty>,
1803}
1804
1805#[skip_serializing_none]
1806#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1807#[serde(default)]
1808pub struct Reporting {
1809 #[serde(rename = "@id")]
1810 pub id: Option<String>,
1811 #[serde(rename = "@schemeIdUri")]
1812 pub schemeIdUri: String,
1813 #[serde(rename = "@value")]
1814 pub value: Option<String>,
1815 #[serde(rename = "@dvb:reportingUrl", alias = "@reportingUrl")]
1816 pub reportingUrl: Option<String>,
1817 #[serde(rename = "@dvb:probability", alias = "@probability")]
1818 pub probability: Option<u64>,
1819}
1820
1821#[skip_serializing_none]
1822#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1823#[serde(default)]
1824pub struct Range {
1825 #[serde(rename = "@starttime",
1826 serialize_with = "serialize_xs_duration",
1827 deserialize_with = "deserialize_xs_duration",
1828 default)]
1829 pub starttime: Option<Duration>,
1830 #[serde(rename = "@duration",
1831 serialize_with = "serialize_xs_duration",
1832 deserialize_with = "deserialize_xs_duration",
1833 default)]
1834 pub duration: Option<Duration>,
1835}
1836
1837#[skip_serializing_none]
1838#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1839#[serde(default)]
1840pub struct Metrics {
1841 #[serde(rename = "@metrics")]
1842 pub metrics: String,
1843 pub Reporting: Vec<Reporting>,
1844 pub Range: Vec<Range>,
1845}
1846
1847#[skip_serializing_none]
1848#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1849#[serde(default)]
1850pub struct Latency {
1851 #[serde(rename = "@min", serialize_with="serialize_opt_xsd_double")]
1852 pub min: Option<f64>,
1853 #[serde(rename = "@max", serialize_with="serialize_opt_xsd_double")]
1854 pub max: Option<f64>,
1855 #[serde(rename = "@target", serialize_with="serialize_opt_xsd_double")]
1856 pub target: Option<f64>,
1857 #[serde(rename = "@referenceId")]
1858 pub referenceId: Option<String>,
1859}
1860
1861#[skip_serializing_none]
1862#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1863#[serde(default)]
1864pub struct PlaybackRate {
1865 #[serde(rename = "@min", serialize_with="serialize_xsd_double")]
1866 pub min: f64,
1867 #[serde(rename = "@max", serialize_with="serialize_xsd_double")]
1868 pub max: f64,
1869}
1870
1871#[skip_serializing_none]
1872#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1873#[serde(default)]
1874pub struct ServiceDescription {
1875 #[serde(rename = "@id")]
1876 pub id: Option<String>,
1877 pub Latency: Option<Latency>,
1878 pub PlaybackRate: Option<PlaybackRate>,
1879 #[serde(rename = "Scope")]
1880 pub scopes: Vec<Scope>,
1881}
1882
1883#[skip_serializing_none]
1885#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1886#[serde(default)]
1887pub struct UTCTiming {
1888 #[serde(rename = "@id")]
1889 pub id: Option<String>,
1890 #[serde(rename = "@schemeIdUri")]
1893 pub schemeIdUri: String,
1894 #[serde(rename = "@value")]
1895 pub value: Option<String>,
1896}
1897
1898#[skip_serializing_none]
1903#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1904#[serde(default)]
1905pub struct ProducerReferenceTime {
1906 #[serde(rename = "@id")]
1908 pub id: Option<String>,
1909 #[serde(rename = "@inband", default = "default_optbool_false")]
1910 pub inband: Option<bool>,
1911 #[serde(rename = "@presentationTime")]
1913 pub presentationTime: Option<u64>,
1914 #[serde(rename = "@type", default = "default_optstring_encoder")]
1915 pub prtType: Option<String>,
1916 #[serde(rename = "@wallClockTime",
1920 alias="@wallclockTime",
1921 deserialize_with = "deserialize_xs_datetime",
1922 default)]
1923 pub wallClockTime: Option<XsDatetime>,
1924 pub UTCTiming: Option<UTCTiming>,
1925}
1926
1927#[skip_serializing_none]
1928#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Hash)]
1929#[serde(default)]
1930pub struct LeapSecondInformation {
1931 #[serde(rename = "@availabilityStartLeapOffset")]
1932 pub availabilityStartLeapOffset: Option<i64>,
1933 #[serde(rename = "@nextAvailabilityStartLeapOffset")]
1934 pub nextAvailabilityStartLeapOffset: Option<i64>,
1935 #[serde(rename = "@nextLeapChangeTime",
1936 deserialize_with = "deserialize_xs_datetime",
1937 default)]
1938 pub nextLeapChangeTime: Option<XsDatetime>,
1939}
1940
1941#[skip_serializing_none]
1948#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1949#[serde(default)]
1950pub struct PatchLocation {
1951 #[serde(rename = "@ttl", serialize_with="serialize_opt_xsd_double")]
1952 pub ttl: Option<f64>,
1953 #[serde(rename = "$text")]
1954 pub content: String,
1955}
1956
1957#[skip_serializing_none]
1959#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
1960#[serde(default)]
1961pub struct MPD {
1962 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
1963 pub xmlns: Option<String>,
1964 #[serde(rename = "@id")]
1965 pub id: Option<String>,
1966 #[serde(rename = "@profiles")]
1967 pub profiles: Option<String>,
1968 #[serde(rename = "@type")]
1971 pub mpdtype: Option<String>,
1972 #[serde(rename = "@availabilityStartTime",
1973 deserialize_with = "deserialize_xs_datetime",
1974 default)]
1975 pub availabilityStartTime: Option<XsDatetime>,
1976 #[serde(rename = "@availabilityEndTime",
1977 deserialize_with = "deserialize_xs_datetime",
1978 default)]
1979 pub availabilityEndTime: Option<XsDatetime>,
1980 #[serde(rename = "@publishTime",
1981 deserialize_with = "deserialize_xs_datetime",
1982 default)]
1983 pub publishTime: Option<XsDatetime>,
1984 #[serde(rename = "@mediaPresentationDuration",
1985 serialize_with = "serialize_xs_duration",
1986 deserialize_with = "deserialize_xs_duration",
1987 default)]
1988 pub mediaPresentationDuration: Option<Duration>,
1989 #[serde(rename = "@minimumUpdatePeriod",
1990 serialize_with = "serialize_xs_duration",
1991 deserialize_with = "deserialize_xs_duration",
1992 default)]
1993 pub minimumUpdatePeriod: Option<Duration>,
1994 #[serde(rename = "@minBufferTime",
1996 serialize_with = "serialize_xs_duration",
1997 deserialize_with = "deserialize_xs_duration",
1998 default)]
1999 pub minBufferTime: Option<Duration>,
2000 #[serde(rename = "@timeShiftBufferDepth",
2003 serialize_with = "serialize_xs_duration",
2004 deserialize_with = "deserialize_xs_duration",
2005 default)]
2006 pub timeShiftBufferDepth: Option<Duration>,
2007 #[serde(rename = "@suggestedPresentationDelay",
2009 serialize_with = "serialize_xs_duration",
2010 deserialize_with = "deserialize_xs_duration",
2011 default)]
2012 pub suggestedPresentationDelay: Option<Duration>,
2013 #[serde(rename = "@maxSegmentDuration",
2014 serialize_with = "serialize_xs_duration",
2015 deserialize_with = "deserialize_xs_duration",
2016 default)]
2017 pub maxSegmentDuration: Option<Duration>,
2018 #[serde(rename = "@maxSubsegmentDuration",
2019 serialize_with = "serialize_xs_duration",
2020 deserialize_with = "deserialize_xs_duration",
2021 default)]
2022 pub maxSubsegmentDuration: Option<Duration>,
2023 #[serialize_always]
2025 #[serde(rename="@xmlns:xsi", alias="@xsi", serialize_with="serialize_xsi_ns")]
2026 pub xsi: Option<String>,
2027 #[serde(alias = "@ext", rename = "@xmlns:ext")]
2028 pub ext: Option<String>,
2029 #[serialize_always]
2031 #[serde(rename="@xmlns:cenc", alias="@cenc", serialize_with="serialize_cenc_ns")]
2032 pub cenc: Option<String>,
2033 #[serialize_always]
2035 #[serde(rename="@xmlns:mspr", alias="@mspr", serialize_with="serialize_mspr_ns")]
2036 pub mspr: Option<String>,
2037 #[serialize_always]
2039 #[serde(rename="@xmlns:xlink", alias="@xlink", serialize_with="serialize_xlink_ns")]
2040 pub xlink: Option<String>,
2041 #[cfg(feature = "scte35")]
2044 #[serialize_always]
2045 #[serde(rename="@xmlns:scte35", alias="@scte35", serialize_with="scte35::serialize_scte35_ns")]
2046 pub scte35: Option<String>,
2047 #[serialize_always]
2050 #[serde(rename="@xmlns:dvb", alias="@dvb", serialize_with="serialize_dvb_ns")]
2051 pub dvb: Option<String>,
2052 #[serde(rename = "@xsi:schemaLocation", alias = "@schemaLocation")]
2053 pub schemaLocation: Option<String>,
2054 #[serde(alias = "@scte214", rename = "@xmlns:scte214")]
2056 pub scte214: Option<String>,
2057 pub ProgramInformation: Vec<ProgramInformation>,
2058 #[serde(rename = "BaseURL")]
2060 pub base_url: Vec<BaseURL>,
2061 #[serde(rename = "Location", default)]
2062 pub locations: Vec<Location>,
2063 pub PatchLocation: Vec<PatchLocation>,
2066 pub ServiceDescription: Vec<ServiceDescription>,
2067 pub ContentProtection: Vec<ContentProtection>,
2069 #[serde(rename = "Period", default)]
2070 pub periods: Vec<Period>,
2071 pub Metrics: Vec<Metrics>,
2072 #[serde(rename = "EssentialProperty")]
2073 pub essential_property: Vec<EssentialProperty>,
2074 #[serde(rename = "SupplementalProperty")]
2075 pub supplemental_property: Vec<SupplementalProperty>,
2076 pub UTCTiming: Vec<UTCTiming>,
2077 pub LeapSecondInformation: Option<LeapSecondInformation>,
2079}
2080
2081impl std::fmt::Display for MPD {
2082 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2083 write!(f, "{}", quick_xml::se::to_string(self).map_err(|_| std::fmt::Error)?)
2084 }
2085}
2086
2087pub fn parse(xml: &str) -> Result<MPD, DashMpdError> {
2089 #[cfg(feature = "warn_ignored_elements")]
2090 {
2091 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2092 let _: MPD = serde_ignored::deserialize(xd, |path| {
2093 warn!("Unused XML element in manifest: {path}");
2094 }).map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2095 }
2096 let xd = &mut quick_xml::de::Deserializer::from_str(xml);
2097 let mpd: MPD = serde_path_to_error::deserialize(xd)
2098 .map_err(|e| DashMpdError::Parsing(e.to_string()))?;
2099 Ok(mpd)
2100}
2101
2102
2103fn is_audio_codec(name: &str) -> bool {
2105 name.starts_with("mp4a") ||
2106 name.starts_with("aac") ||
2107 name.starts_with("vorbis") ||
2108 name.starts_with("opus") ||
2109 name.starts_with("flac") ||
2110 name.starts_with("mp3") ||
2111 name.starts_with("ec-3") ||
2112 name.starts_with("ac-4") ||
2113 name.starts_with("dtsc") ||
2114 name.starts_with("mha1") }
2116
2117
2118pub fn is_audio_adaptation(a: &&AdaptationSet) -> bool {
2124 if let Some(codec) = &a.codecs {
2125 if is_audio_codec(codec) {
2126 return true;
2127 }
2128 }
2129 if let Some(ct) = &a.contentType {
2130 if ct == "audio" {
2131 return true;
2132 }
2133 }
2134 if let Some(mimetype) = &a.mimeType {
2135 if mimetype.starts_with("audio/") {
2136 return true;
2137 }
2138 }
2139 for r in a.representations.iter() {
2140 if let Some(ct) = &r.contentType {
2141 if ct == "audio" {
2142 return true;
2143 }
2144 }
2145 if let Some(mimetype) = &r.mimeType {
2146 if mimetype.starts_with("audio/") {
2147 return true;
2148 }
2149 }
2150 }
2151 false
2152}
2153
2154pub fn is_video_adaptation(a: &&AdaptationSet) -> bool {
2163 if is_audio_adaptation(a) {
2164 return false;
2165 }
2166 if let Some(ct) = &a.contentType {
2167 if ct == "video" {
2168 return true;
2169 }
2170 }
2171 if let Some(mimetype) = &a.mimeType {
2172 if mimetype.starts_with("video/") {
2173 return true;
2174 }
2175 }
2176 for r in a.representations.iter() {
2177 if let Some(ct) = &r.contentType {
2178 if ct == "video" {
2179 return true;
2180 }
2181 }
2182 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2185 return false;
2186 }
2187 if let Some(mimetype) = &r.mimeType {
2188 if mimetype.starts_with("video/") {
2189 return true;
2190 }
2191 }
2192 }
2193 false
2194}
2195
2196
2197fn is_subtitle_mimetype(mt: &str) -> bool {
2198 mt.eq("text/vtt") ||
2199 mt.eq("application/ttml+xml") ||
2200 mt.eq("application/x-sami")
2201
2202 }
2205
2206fn is_subtitle_codec(c: &str) -> bool {
2207 c == "wvtt" ||
2208 c == "c608" ||
2209 c == "stpp" ||
2210 c == "tx3g" ||
2211 c.starts_with("stpp.")
2212}
2213
2214pub fn is_subtitle_adaptation(a: &&AdaptationSet) -> bool {
2226 if a.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2227 return true;
2228 }
2229 if a.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2230 return true;
2231 }
2232 if a.codecs.as_deref().is_some_and(is_subtitle_codec) {
2233 return true;
2234 }
2235 for cc in a.ContentComponent.iter() {
2236 if cc.contentType.as_deref().is_some_and(|ct| ct.eq("text")) {
2237 return true;
2238 }
2239 }
2240 for r in a.representations.iter() {
2241 if r.mimeType.as_deref().is_some_and(is_subtitle_mimetype) {
2242 return true;
2243 }
2244 if r.codecs.as_deref().is_some_and(is_subtitle_codec) {
2246 return true;
2247 }
2248 }
2249 false
2250}
2251
2252
2253#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2255pub enum SubtitleType {
2256 Vtt,
2258 Srt,
2260 Sub,
2262 Ass,
2264 Ttxt,
2266 Ttml,
2268 Sami,
2270 Wvtt,
2274 Stpp,
2276 Eia608,
2278 Unknown,
2279}
2280
2281fn subtitle_type_for_mimetype(mt: &str) -> Option<SubtitleType> {
2282 match mt {
2283 "text/vtt" => Some(SubtitleType::Vtt),
2284 "application/ttml+xml" => Some(SubtitleType::Ttml),
2285 "application/x-sami" => Some(SubtitleType::Sami),
2286 _ => None
2287 }
2288}
2289
2290pub fn subtitle_type(a: &&AdaptationSet) -> SubtitleType {
2291 if let Some(mimetype) = &a.mimeType {
2292 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2293 return st;
2294 }
2295 }
2296 if let Some(codecs) = &a.codecs {
2297 if codecs == "wvtt" {
2298 return SubtitleType::Wvtt;
2300 }
2301 if codecs == "c608" {
2302 return SubtitleType::Eia608;
2303 }
2304 if codecs == "tx3g" {
2305 return SubtitleType::Ttxt;
2306 }
2307 if codecs == "stpp" {
2308 return SubtitleType::Stpp;
2309 }
2310 if codecs.starts_with("stpp.") {
2311 return SubtitleType::Stpp;
2312 }
2313 }
2314 for r in a.representations.iter() {
2315 if let Some(mimetype) = &r.mimeType {
2316 if let Some(st) = subtitle_type_for_mimetype(mimetype) {
2317 return st;
2318 }
2319 }
2320 if let Some(codecs) = &r.codecs {
2321 if codecs == "wvtt" {
2322 return SubtitleType::Wvtt;
2323 }
2324 if codecs == "c608" {
2325 return SubtitleType::Eia608;
2326 }
2327 if codecs == "tx3g" {
2328 return SubtitleType::Ttxt;
2329 }
2330 if codecs == "stpp" {
2331 return SubtitleType::Stpp;
2332 }
2333 if codecs.starts_with("stpp.") {
2334 return SubtitleType::Stpp;
2335 }
2336 }
2337 }
2338 SubtitleType::Unknown
2339}
2340
2341
2342#[allow(dead_code)]
2343fn content_protection_type(cp: &ContentProtection) -> String {
2344 if let Some(v) = &cp.value {
2345 if v.eq("cenc") {
2346 return String::from("cenc");
2347 }
2348 if v.eq("Widevine") {
2349 return String::from("Widevine");
2350 }
2351 if v.eq("MSPR 2.0") {
2352 return String::from("PlayReady");
2353 }
2354 }
2355 let uri = &cp.schemeIdUri;
2357 let uri = uri.to_lowercase();
2358 if uri.eq("urn:mpeg:dash:mp4protection:2011") {
2359 return String::from("cenc");
2360 }
2361 if uri.eq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed") {
2362 return String::from("Widevine");
2363 }
2364 if uri.eq("urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95") {
2365 return String::from("PlayReady");
2366 }
2367 if uri.eq("urn:uuid:94ce86fb-07ff-4f43-adb8-93d2fa968ca2") {
2368 return String::from("FairPlay");
2369 }
2370 if uri.eq("urn:uuid:3ea8778f-7742-4bf9-b18b-e834b2acbd47") {
2371 return String::from("Clear Key AES-128");
2372 }
2373 if uri.eq("urn:uuid:be58615b-19c4-4684-88b3-c8c57e99e957") {
2374 return String::from("Clear Key SAMPLE-AES");
2375 }
2376 if uri.eq("urn:uuid:adb41c24-2dbf-4a6d-958b-4457c0d27b95") {
2377 return String::from("Nagra");
2378 }
2379 if uri.eq("urn:uuid:5e629af5-38da-4063-8977-97ffbd9902d4") {
2380 return String::from("Marlin");
2381 }
2382 if uri.eq("urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb") {
2383 return String::from("Adobe PrimeTime");
2384 }
2385 if uri.eq("urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b") {
2386 return String::from("W3C Common PSSH box");
2387 }
2388 if uri.eq("urn:uuid:80a6be7e-1448-4c37-9e70-d5aebe04c8d2") {
2389 return String::from("Irdeto Content Protection");
2390 }
2391 if uri.eq("urn:uuid:3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c") {
2392 return String::from("WisePlay-ChinaDRM");
2393 }
2394 if uri.eq("urn:uuid:616c7469-6361-7374-2d50-726f74656374") {
2395 return String::from("Alticast");
2396 }
2397 if uri.eq("urn:uuid:6dd8b3c3-45f4-4a68-bf3a-64168d01a4a6") {
2398 return String::from("ABV DRM");
2399 }
2400 if uri.eq("urn:mpeg:dash:sea:2012") {
2402 return String::from("SEA");
2403 }
2404 String::from("<unknown>")
2405}
2406
2407
2408fn check_segment_template_duration(
2409 st: &SegmentTemplate,
2410 max_seg_duration: &Duration,
2411 outer_timescale: u64) -> Vec<String>
2412{
2413 let mut errors = Vec::new();
2414 if let Some(timeline) = &st.SegmentTimeline {
2415 for s in &timeline.segments {
2416 let sd = s.d / st.timescale.unwrap_or(outer_timescale);
2417 if sd > max_seg_duration.as_secs() {
2418 errors.push(String::from("SegmentTimeline has segment@d > @maxSegmentDuration"));
2419 }
2420 }
2421 }
2422 errors
2423}
2424
2425fn check_segment_template_conformity(st: &SegmentTemplate) -> Vec<String> {
2426 let mut errors = Vec::new();
2427 if let Some(md) = &st.media {
2428 if !valid_url_p(md) {
2429 errors.push(format!("invalid URL {md}"));
2430 }
2431 if md.contains("$Number$") && md.contains("$Time") {
2432 errors.push(String::from("both $Number$ and $Time$ are used in media template URL"));
2433 }
2434 }
2435 if let Some(init) = &st.initialization {
2436 if !valid_url_p(init) {
2437 errors.push(format!("invalid URL {init}"));
2438 }
2439 if init.contains("$Number") {
2440 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2441 }
2442 if init.contains("$Time") {
2443 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2444 }
2445 }
2446 if st.duration.is_some() && st.SegmentTimeline.is_some() {
2447 errors.push(String::from("both SegmentTemplate.duration and SegmentTemplate.SegmentTimeline present"));
2448 }
2449 errors
2450}
2451
2452
2453fn valid_url_p(u: &str) -> bool {
2456 use url::ParseError;
2457
2458 match Url::parse(u) {
2459 Ok(url) => {
2460 url.scheme() == "https" ||
2461 url.scheme() == "http" ||
2462 url.scheme() == "ftp" ||
2463 url.scheme() == "file" ||
2464 url.scheme() == "data"
2465 },
2466 Err(ParseError::RelativeUrlWithoutBase) => true,
2467 Err(_) => false,
2468 }
2469}
2470
2471pub fn check_conformity(mpd: &MPD) -> Vec<String> {
2473 let mut errors = Vec::new();
2474
2475 for p in &mpd.periods {
2478 if p.adaptations.is_empty() {
2479 errors.push(format!("Period with @id {} contains no AdaptationSet elements",
2480 p.id.clone().unwrap_or(String::from("<unspecified>"))));
2481 }
2482 for a in &p.adaptations {
2483 if let Some(mh) = a.maxHeight {
2484 if let Some(mr) = a.representations.iter().max_by_key(|r| r.height.unwrap_or(0)) {
2485 if mr.height.unwrap_or(0) > mh {
2486 errors.push(String::from("invalid @maxHeight on AdaptationSet"));
2487 }
2488 }
2489 }
2490 }
2491 }
2492 for p in &mpd.periods {
2495 for a in &p.adaptations {
2496 if let Some(mw) = a.maxWidth {
2497 if let Some(mr) = a.representations.iter().max_by_key(|r| r.width.unwrap_or(0)) {
2498 if mr.width.unwrap_or(0) > mw {
2499 errors.push(String::from("invalid @maxWidth on AdaptationSet"));
2500 }
2501 }
2502 }
2503 }
2504 }
2505 for p in &mpd.periods {
2508 for a in &p.adaptations {
2509 if let Some(mb) = a.maxBandwidth {
2510 if let Some(mr) = a.representations.iter().max_by_key(|r| r.bandwidth.unwrap_or(0)) {
2511 if mr.bandwidth.unwrap_or(0) > mb {
2512 errors.push(String::from("invalid @maxBandwidth on AdaptationSet"));
2513 }
2514 }
2515 }
2516 }
2517 }
2518 if let Some(max_seg_duration) = mpd.maxSegmentDuration {
2520 for p in &mpd.periods {
2521 for a in &p.adaptations {
2522 let mut outer_timescale = 1;
2533 if let Some(st) = &a.SegmentTemplate {
2534 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2535 .into_iter()
2536 .for_each(|msg| errors.push(msg));
2537 if let Some(ots) = st.timescale {
2538 outer_timescale = ots;
2539 }
2540 }
2541 for r in &a.representations {
2542 if let Some(st) = &r.SegmentTemplate {
2543 check_segment_template_duration(st, &max_seg_duration, outer_timescale)
2544 .into_iter()
2545 .for_each(|msg| errors.push(msg));
2546 }
2547 }
2548 }
2549 }
2550 }
2551
2552 for bu in &mpd.base_url {
2553 if !valid_url_p(&bu.base) {
2554 errors.push(format!("invalid URL {}", &bu.base));
2555 }
2556 }
2557 for p in &mpd.periods {
2558 for bu in &p.BaseURL {
2559 if !valid_url_p(&bu.base) {
2560 errors.push(format!("invalid URL {}", &bu.base));
2561 }
2562 }
2563 for a in &p.adaptations {
2564 for bu in &a.BaseURL {
2565 if !valid_url_p(&bu.base) {
2566 errors.push(format!("invalid URL {}", &bu.base));
2567 }
2568 }
2569 if let Some(st) = &a.SegmentTemplate {
2570 check_segment_template_conformity(st)
2571 .into_iter()
2572 .for_each(|msg| errors.push(msg));
2573 }
2574 for r in &a.representations {
2575 for bu in &r.BaseURL {
2576 if !valid_url_p(&bu.base) {
2577 errors.push(format!("invalid URL {}", &bu.base));
2578 }
2579 }
2580 if let Some(sb) = &r.SegmentBase {
2581 if let Some(init) = &sb.Initialization {
2582 if let Some(su) = &init.sourceURL {
2583 if !valid_url_p(su) {
2584 errors.push(format!("invalid URL {su}"));
2585 }
2586 if su.contains("$Number") {
2587 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2588 }
2589 if su.contains("$Time") {
2590 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2591 }
2592 }
2593 }
2594 if let Some(ri) = &sb.representation_index {
2595 if let Some(su) = &ri.sourceURL {
2596 if !valid_url_p(su) {
2597 errors.push(format!("invalid URL {su}"));
2598 }
2599 }
2600 }
2601 }
2602 if let Some(sl) = &r.SegmentList {
2603 if let Some(hr) = &sl.href {
2604 if !valid_url_p(hr) {
2605 errors.push(format!("invalid URL {hr}"));
2606 }
2607 }
2608 if let Some(init) = &sl.Initialization {
2609 if let Some(su) = &init.sourceURL {
2610 if !valid_url_p(su) {
2611 errors.push(format!("invalid URL {su}"));
2612 }
2613 if su.contains("$Number") {
2614 errors.push(String::from("$Number$ identifier used in initialization segment URL"));
2615 }
2616 if su.contains("$Time") {
2617 errors.push(String::from("$Time$ identifier used in initialization segment URL"));
2618 }
2619 }
2620 }
2621 for su in &sl.segment_urls {
2622 if let Some(md) = &su.media {
2623 if !valid_url_p(md) {
2624 errors.push(format!("invalid URL {md}"));
2625 }
2626 }
2627 if let Some(ix) = &su.index {
2628 if !valid_url_p(ix) {
2629 errors.push(format!("invalid URL {ix}"));
2630 }
2631 }
2632 }
2633 }
2634 if let Some(st) = &r.SegmentTemplate {
2635 check_segment_template_conformity(st)
2636 .into_iter()
2637 .for_each(|msg| errors.push(msg));
2638 }
2639 }
2640 }
2641 }
2642 for pi in &mpd.ProgramInformation {
2643 if let Some(u) = &pi.moreInformationURL {
2644 if !valid_url_p(u) {
2645 errors.push(format!("invalid URL {u}"));
2646 }
2647 }
2648 }
2649 errors
2650}
2651
2652#[cfg(test)]
2653mod tests {
2654 use proptest::prelude::*;
2655
2656 proptest! {
2657 #[test]
2658 fn doesnt_crash(s in "\\PC*") {
2659 let _ = super::parse_xs_duration(&s);
2660 let _ = super::parse_xs_datetime(&s);
2661 }
2662 }
2663
2664 #[test]
2665 fn test_parse_xs_duration() {
2666 use std::time::Duration;
2667 use super::parse_xs_duration;
2668
2669 assert!(parse_xs_duration("").is_err());
2670 assert!(parse_xs_duration("foobles").is_err());
2671 assert!(parse_xs_duration("P").is_err());
2672 assert!(parse_xs_duration("PW").is_err());
2673 assert!(parse_xs_duration("-PT4.5S").is_err());
2675 assert!(parse_xs_duration("1Y2M3DT4H5M6S").is_err()); assert_eq!(parse_xs_duration("PT3H11M53S").ok(), Some(Duration::new(11513, 0)));
2677 assert_eq!(parse_xs_duration("PT42M30S").ok(), Some(Duration::new(2550, 0)));
2678 assert_eq!(parse_xs_duration("PT30M38S").ok(), Some(Duration::new(1838, 0)));
2679 assert_eq!(parse_xs_duration("PT0H10M0.00S").ok(), Some(Duration::new(600, 0)));
2680 assert_eq!(parse_xs_duration("PT1.5S").ok(), Some(Duration::new(1, 500_000_000)));
2681 assert_eq!(parse_xs_duration("PT1.500S").ok(), Some(Duration::new(1, 500_000_000)));
2682 assert_eq!(parse_xs_duration("PT1.500000000S").ok(), Some(Duration::new(1, 500_000_000)));
2683 assert_eq!(parse_xs_duration("PT0S").ok(), Some(Duration::new(0, 0)));
2684 assert_eq!(parse_xs_duration("PT0.001S").ok(), Some(Duration::new(0, 1_000_000)));
2685 assert_eq!(parse_xs_duration("PT0.00100S").ok(), Some(Duration::new(0, 1_000_000)));
2686 assert_eq!(parse_xs_duration("PT344S").ok(), Some(Duration::new(344, 0)));
2687 assert_eq!(parse_xs_duration("PT634.566S").ok(), Some(Duration::new(634, 566_000_000)));
2688 assert_eq!(parse_xs_duration("PT72H").ok(), Some(Duration::new(72*60*60, 0)));
2689 assert_eq!(parse_xs_duration("PT0H0M30.030S").ok(), Some(Duration::new(30, 30_000_000)));
2690 assert_eq!(parse_xs_duration("PT1004199059S").ok(), Some(Duration::new(1004199059, 0)));
2691 assert_eq!(parse_xs_duration("P0Y20M0D").ok(), Some(Duration::new(51840000, 0)));
2692 assert_eq!(parse_xs_duration("PT1M30.5S").ok(), Some(Duration::new(90, 500_000_000)));
2693 assert_eq!(parse_xs_duration("PT10M10S").ok(), Some(Duration::new(610, 0)));
2694 assert_eq!(parse_xs_duration("PT1H0.040S").ok(), Some(Duration::new(3600, 40_000_000)));
2695 assert_eq!(parse_xs_duration("PT00H03M30SZ").ok(), Some(Duration::new(210, 0)));
2696 assert_eq!(parse_xs_duration("PT3.14159S").ok(), Some(Duration::new(3, 141_590_000)));
2697 assert_eq!(parse_xs_duration("PT3.14159265S").ok(), Some(Duration::new(3, 141_592_650)));
2698 assert_eq!(parse_xs_duration("PT3.141592653S").ok(), Some(Duration::new(3, 141_592_653)));
2699 assert_eq!(parse_xs_duration("PT3.141592653897S").ok(), Some(Duration::new(3, 141_592_653)));
2701 assert_eq!(parse_xs_duration("P0W").ok(), Some(Duration::new(0, 0)));
2702 assert_eq!(parse_xs_duration("P26W").ok(), Some(Duration::new(15724800, 0)));
2703 assert_eq!(parse_xs_duration("P52W").ok(), Some(Duration::new(31449600, 0)));
2704 assert_eq!(parse_xs_duration("P10D").ok(), Some(Duration::new(864000, 0)));
2705 assert_eq!(parse_xs_duration("P0Y").ok(), Some(Duration::new(0, 0)));
2706 assert_eq!(parse_xs_duration("P1Y").ok(), Some(Duration::new(31536000, 0)));
2707 assert_eq!(parse_xs_duration("P1Y0W0S").ok(), Some(Duration::new(31536000, 0)));
2708 assert_eq!(parse_xs_duration("PT4H").ok(), Some(Duration::new(14400, 0)));
2709 assert_eq!(parse_xs_duration("+PT4H").ok(), Some(Duration::new(14400, 0)));
2710 assert_eq!(parse_xs_duration("PT0004H").ok(), Some(Duration::new(14400, 0)));
2711 assert_eq!(parse_xs_duration("PT4H0M").ok(), Some(Duration::new(14400, 0)));
2712 assert_eq!(parse_xs_duration("PT4H0S").ok(), Some(Duration::new(14400, 0)));
2713 assert_eq!(parse_xs_duration("P23DT23H").ok(), Some(Duration::new(2070000, 0)));
2714 assert_eq!(parse_xs_duration("P0Y0M0DT0H4M20.880S").ok(), Some(Duration::new(260, 880_000_000)));
2715 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6.7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2716 assert_eq!(parse_xs_duration("P1Y2M3DT4H5M6,7S").ok(), Some(Duration::new(36993906, 700_000_000)));
2717
2718 }
2722
2723 #[test]
2724 fn test_serialize_xs_duration() {
2725 use std::time::Duration;
2726 use super::MPD;
2727
2728 fn serialized_xs_duration(d: Duration) -> String {
2729 let mpd = MPD {
2730 minBufferTime: Some(d),
2731 ..Default::default()
2732 };
2733 let xml = mpd.to_string();
2734 let doc = roxmltree::Document::parse(&xml).unwrap();
2735 String::from(doc.root_element().attribute("minBufferTime").unwrap())
2736 }
2737
2738 assert_eq!("PT0S", serialized_xs_duration(Duration::new(0, 0)));
2739 assert_eq!("PT0.001S", serialized_xs_duration(Duration::new(0, 1_000_000)));
2740 assert_eq!("PT42S", serialized_xs_duration(Duration::new(42, 0)));
2741 assert_eq!("PT1.5S", serialized_xs_duration(Duration::new(1, 500_000_000)));
2742 assert_eq!("PT30.03S", serialized_xs_duration(Duration::new(30, 30_000_000)));
2743 assert_eq!("PT1M30.5S", serialized_xs_duration(Duration::new(90, 500_000_000)));
2744 assert_eq!("PT5M44S", serialized_xs_duration(Duration::new(344, 0)));
2745 assert_eq!("PT42M30S", serialized_xs_duration(Duration::new(2550, 0)));
2746 assert_eq!("PT30M38S", serialized_xs_duration(Duration::new(1838, 0)));
2747 assert_eq!("PT10M10S", serialized_xs_duration(Duration::new(610, 0)));
2748 assert_eq!("PT1H0M0.04S", serialized_xs_duration(Duration::new(3600, 40_000_000)));
2749 assert_eq!("PT3H11M53S", serialized_xs_duration(Duration::new(11513, 0)));
2750 assert_eq!("PT4H0M0S", serialized_xs_duration(Duration::new(14400, 0)));
2751 }
2752
2753 #[test]
2754 fn test_parse_xs_datetime() {
2755 use chrono::{DateTime, NaiveDate};
2756 use chrono::offset::Utc;
2757 use super::parse_xs_datetime;
2758
2759 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2760 .unwrap()
2761 .and_hms_opt(1, 3, 2)
2762 .unwrap();
2763 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02Z").ok(),
2764 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2765 let date = NaiveDate::from_ymd_opt(2023, 4, 19)
2766 .unwrap()
2767 .and_hms_nano_opt(1, 3, 2, 958*1000*1000)
2768 .unwrap();
2769 assert_eq!(parse_xs_datetime("2023-04-19T01:03:02.958Z").ok(),
2770 Some(DateTime::<Utc>::from_naive_utc_and_offset(date, Utc)));
2771 }
2772}