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;
18use broadcast_common::units::MICROS_PER_SEC;
19
20pub const MIN_PRESENTATION_DELAY_US: i128 = 4 * (MICROS_PER_SEC as i128);
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 .unwrap_or(0)
91 }
92 };
93 count.checked_sub(1)
94}
95
96#[must_use]
99pub fn oldest_in_window(plan: &SegmentPlan, utc_now_us: i128) -> Option<u64> {
100 let newest = newest_complete(plan, utc_now_us)?;
101 let expired = plan
102 .leading_true(newest + 1, |s| {
103 s.available_until_us.is_some_and(|u| u < utc_now_us)
104 })
105 .unwrap_or(0);
106 (expired <= newest).then_some(expired)
107}
108
109#[must_use]
111pub fn window(plan: &SegmentPlan, utc_now_us: i128) -> Option<LiveWindow> {
112 Some(LiveWindow {
113 oldest: oldest_in_window(plan, utc_now_us)?,
114 newest: newest_complete(plan, utc_now_us)?,
115 })
116}
117
118#[must_use]
121pub fn join_index(plan: &SegmentPlan, utc_now_us: i128, delay_us: i128) -> Option<u64> {
122 let w = window(plan, utc_now_us)?;
123 let (ast, _) = plan.avail_parts()?;
124 let t = join_time_us(utc_now_us, delay_us, ast, plan.period_start_us());
125 let at = match plan.index_at_or_before(t) {
126 Ok(Some(i)) => i,
127 _ => w.oldest,
128 };
129 Some(at.clamp(w.oldest, w.newest))
130}