dash_runtime/timing/
live_edge.rs1use std::time::Duration;
14
15use crate::address::plan::SegmentPlan;
16use crate::timing::availability::complete_count_constant;
17use crate::timing::period::duration_us;
18
19const MICROS_PER_SECOND: i128 = 1_000_000;
20pub const MIN_PRESENTATION_DELAY_US: i128 = 4 * MICROS_PER_SECOND;
22pub const DELAY_SEGMENT_MULTIPLE: i128 = 3;
24pub const TSB_GUARD_SEGMENTS: i128 = 2;
26
27#[must_use]
29pub fn presentation_delay_us(
30 suggested: Option<Duration>,
31 max_segment_duration_us: i128,
32 time_shift_buffer_depth_us: Option<i128>,
33 override_us: Option<i128>,
34) -> i128 {
35 if let Some(o) = override_us {
36 return o.max(0);
37 }
38 let mut delay = suggested.map_or_else(
39 || {
40 MIN_PRESENTATION_DELAY_US
41 .max(max_segment_duration_us.saturating_mul(DELAY_SEGMENT_MULTIPLE))
42 },
43 duration_us,
44 );
45 if let Some(tsb) = time_shift_buffer_depth_us {
46 let ceiling =
47 tsb.saturating_sub(max_segment_duration_us.saturating_mul(TSB_GUARD_SEGMENTS));
48 delay = delay.min(ceiling);
49 }
50 delay.max(0)
51}
52
53#[must_use]
55pub fn join_time_us(utc_now_us: i128, delay_us: i128, ast_us: i128, period_start_us: i128) -> i128 {
56 utc_now_us
57 .saturating_sub(delay_us)
58 .saturating_sub(ast_us.saturating_add(period_start_us))
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63#[non_exhaustive]
64pub struct LiveWindow {
65 pub oldest: u64,
67 pub newest: u64,
69}
70
71#[must_use]
74pub fn newest_complete(plan: &SegmentPlan, utc_now_us: i128) -> Option<u64> {
75 let (ast, ato) = plan.avail_parts()?;
76 let count = match (plan.len(), plan.constant_duration()) {
77 (None, Some(d)) => complete_count_constant(
78 utc_now_us,
79 ast,
80 plan.period_start_us(),
81 ato,
82 d,
83 plan.timescale(),
84 ),
85 (len, _) => {
86 let bound = len.unwrap_or(u64::MAX / 2);
87 plan.leading_true(bound, |s| {
88 s.available_from_us.is_some_and(|a| a <= utc_now_us)
89 })
90 }
91 };
92 count.checked_sub(1)
93}
94
95#[must_use]
98pub fn oldest_in_window(plan: &SegmentPlan, utc_now_us: i128) -> Option<u64> {
99 let newest = newest_complete(plan, utc_now_us)?;
100 let expired = plan.leading_true(newest + 1, |s| {
101 s.available_until_us.is_some_and(|u| u < utc_now_us)
102 });
103 (expired <= newest).then_some(expired)
104}
105
106#[must_use]
108pub fn window(plan: &SegmentPlan, utc_now_us: i128) -> Option<LiveWindow> {
109 Some(LiveWindow {
110 oldest: oldest_in_window(plan, utc_now_us)?,
111 newest: newest_complete(plan, utc_now_us)?,
112 })
113}
114
115#[must_use]
118pub fn join_index(plan: &SegmentPlan, utc_now_us: i128, delay_us: i128) -> Option<u64> {
119 let w = window(plan, utc_now_us)?;
120 let (ast, _) = plan.avail_parts()?;
121 let t = join_time_us(utc_now_us, delay_us, ast, plan.period_start_us());
122 let at = plan.index_at_or_before(t).unwrap_or(w.oldest);
123 Some(at.clamp(w.oldest, w.newest))
124}