dash_runtime/timing/
period.rs1use std::time::Duration;
15
16use broadcast_common::impl_spec_display;
17
18use crate::mpd::model::{Mpd, PresentationType};
19
20const MICROS_PER_SECOND: i128 = 1_000_000;
21const NANOS_PER_MICRO: u32 = 1_000;
22
23#[must_use]
25pub fn duration_us(d: Duration) -> i128 {
26 i128::from(d.as_secs()) * MICROS_PER_SECOND + i128::from(d.subsec_nanos() / NANOS_PER_MICRO)
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31#[non_exhaustive]
32pub enum PeriodKind {
33 Regular,
35 EarlyTerminated,
37 EarlyAvailable,
39}
40
41impl PeriodKind {
42 #[must_use]
44 pub fn name(&self) -> &'static str {
45 match self {
46 Self::Regular => "regular",
47 Self::EarlyTerminated => "early_terminated",
48 Self::EarlyAvailable => "early_available",
49 }
50 }
51}
52impl_spec_display!(PeriodKind);
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56#[non_exhaustive]
57pub struct PeriodTiming {
58 pub start_us: Option<i128>,
61 pub end_us: Option<i128>,
63 pub kind: PeriodKind,
65}
66
67#[must_use]
69pub fn period_timings(mpd: &Mpd) -> Vec<PeriodTiming> {
70 let mut out: Vec<PeriodTiming> = Vec::with_capacity(mpd.periods.len());
71 let starts: Vec<Option<i128>> = {
72 let mut starts: Vec<Option<i128>> = Vec::with_capacity(mpd.periods.len());
73 for (i, p) in mpd.periods.iter().enumerate() {
74 let start = if let Some(s) = p.start {
75 Some(duration_us(s))
76 } else if i == 0 {
77 (mpd.kind == PresentationType::Static).then_some(0)
78 } else {
79 let prev = &mpd.periods[i - 1];
80 match (prev.duration, starts[i - 1]) {
81 (Some(d), Some(ps)) => Some(ps.saturating_add(duration_us(d))),
82 _ => None,
83 }
84 };
85 starts.push(start);
86 }
87 starts
88 };
89 for (i, p) in mpd.periods.iter().enumerate() {
90 let start = starts[i];
91 let next_has_start = mpd.periods.get(i + 1).is_some_and(|n| n.start.is_some());
92 let early_terminated =
93 p.duration.is_some() && (next_has_start || mpd.minimum_update_period.is_some());
94 let kind = match start {
95 None if mpd.kind == PresentationType::Dynamic => PeriodKind::EarlyAvailable,
96 _ if early_terminated => PeriodKind::EarlyTerminated,
97 _ => PeriodKind::Regular,
98 };
99 let next_start = starts.get(i + 1).copied().flatten();
100 let end = match (start, p.duration) {
101 (Some(s), Some(d)) => Some(s.saturating_add(duration_us(d))),
102 (Some(_), None) => next_start.or_else(|| {
103 (i + 1 == mpd.periods.len())
104 .then(|| mpd.media_presentation_duration.map(duration_us))
105 .flatten()
106 }),
107 (None, _) => None,
108 };
109 out.push(PeriodTiming {
110 start_us: start,
111 end_us: end,
112 kind,
113 });
114 }
115 out
116}