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