1use super::stats::MediaStats;
6use super::FRAME_MS;
7use std::collections::BTreeMap;
8use std::sync::atomic::Ordering;
9
10pub const PREFILL: usize = 2;
12pub const MAX_DEPTH: usize = 25;
14pub const MAX_TARGET: usize = 20;
16const PEAK_DECAY_MS: f32 = 0.4;
19const SHED_AFTER_MS: u64 = 3000;
22pub const MAX_PLC_RUN: u32 = 3;
26
27pub enum Pop {
28 Frame(Vec<u8>),
29 Fec(Vec<u8>),
31 Muted,
32 Conceal,
33 Wait,
34}
35
36pub struct Buffered {
37 payload: Vec<u8>,
38 muted: bool,
39}
40
41#[derive(Default)]
42pub struct Jitter {
43 frames: BTreeMap<u64, Buffered>,
44 next: Option<u64>,
46 newest: Option<u64>,
48 last_arrival: Option<(u64, u64)>,
50 jitter_ms: f32,
52 peak_ms: f32,
54 over_since: Option<u64>,
56 shed_after: Option<u64>,
57}
58
59impl Jitter {
60 pub fn buffered(&self) -> usize {
62 self.frames.len()
63 }
64
65 fn target_frames(&self) -> usize {
68 let need_ms = (self.jitter_ms * 3.0).max(self.peak_ms * 1.2);
69 let frames = (need_ms / FRAME_MS as f32).ceil() as usize;
70 (PREFILL + frames).clamp(PREFILL, MAX_TARGET)
71 }
72
73 fn unwrap_seq(&mut self, seq: u16) -> u64 {
74 let s = match self.newest {
75 None => seq as u64,
76 Some(newest) => {
77 let delta = seq.wrapping_sub(newest as u16) as i16 as i64;
78 (newest as i64 + delta).max(0) as u64
79 }
80 };
81 if self.newest.map_or(true, |n| s > n) {
82 self.newest = Some(s);
83 }
84 s
85 }
86
87 pub fn push(&mut self, seq: u16, muted: bool, payload: &[u8], now_ms: u64, stats: &MediaStats) {
89 let s = self.unwrap_seq(seq);
90 let now = now_ms;
91 if let Some((at, prev_seq)) = self.last_arrival {
92 let expected = (s as i64 - prev_seq as i64) as f32 * FRAME_MS as f32;
93 let actual = now.saturating_sub(at) as f32;
94 let d = (actual - expected).abs();
95 self.jitter_ms += (d - self.jitter_ms) / 16.0;
96 self.peak_ms = d.max(self.peak_ms - PEAK_DECAY_MS);
97 stats.jitter_ms.store(self.jitter_ms as u32, Ordering::Relaxed);
98 }
99 self.last_arrival = Some((now, s));
100
101 if let Some(next) = self.next {
102 if s < next {
103 stats.late.fetch_add(1, Ordering::Relaxed);
104 return;
105 }
106 }
107 self.frames.insert(s, Buffered { payload: payload.to_vec(), muted });
108
109 if self.frames.len() > MAX_DEPTH {
110 let keep_from = *self.frames.keys().nth(self.frames.len() - PREFILL).unwrap();
112 let dropped = self.frames.range(..keep_from).count() as u64;
113 self.frames = self.frames.split_off(&keep_from);
114 stats.lost.fetch_add(dropped, Ordering::Relaxed);
115 self.next = Some(keep_from);
116 }
117 }
118
119 pub fn pop(&mut self, plc_run: u32, now_ms: u64, stats: &MediaStats) -> Pop {
121 let next = match self.next {
122 Some(n) => n,
123 None => {
124 if self.frames.len() < PREFILL {
125 return Pop::Wait;
126 }
127 let first = *self.frames.keys().next().unwrap();
128 self.next = Some(first);
129 first
130 }
131 };
132 if self.frames.len() > self.target_frames() {
133 let now = now_ms;
136 let since = *self.over_since.get_or_insert(now);
137 if now.saturating_sub(since) >= self.shed_after.unwrap_or(SHED_AFTER_MS) {
138 if let Some(oldest) = self.frames.keys().next().copied() {
139 self.frames.remove(&oldest);
140 stats.shed.fetch_add(1, Ordering::Relaxed);
141 self.next = Some(oldest + 1);
142 self.over_since = Some(now);
143 return self.pop(plc_run, now_ms, stats);
144 }
145 }
146 } else {
147 self.over_since = None;
148 }
149 if let Some(f) = self.frames.remove(&next) {
150 self.next = Some(next + 1);
151 return if f.muted { Pop::Muted } else { Pop::Frame(f.payload) };
152 }
153 match self.frames.keys().next().copied() {
154 Some(min) if min == next + 1 => {
155 if plc_run == 0 && self.frames.len() < self.target_frames() {
158 return Pop::Conceal;
159 }
160 self.next = Some(min);
161 Pop::Fec(self.frames[&min].payload.clone())
162 }
163 Some(min) => {
164 if plc_run < 2 {
166 self.next = Some(next + 1);
167 Pop::Conceal
168 } else {
169 stats.lost.fetch_add(min - next, Ordering::Relaxed);
170 self.next = Some(min);
171 self.pop(0, now_ms, stats)
172 }
173 }
174 None => {
175 if plc_run < MAX_PLC_RUN {
178 Pop::Conceal
179 } else {
180 Pop::Wait
181 }
182 }
183 }
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 fn frame(n: u8) -> Vec<u8> {
192 vec![n]
193 }
194
195 #[test]
196 fn frames_play_in_order_after_prefill_and_a_gap_uses_fec() {
197 let stats = MediaStats::default();
198 let mut j = Jitter::default();
199 assert!(matches!(j.pop(0, 0, &stats), Pop::Wait));
200 j.push(0, false, &frame(0), 0, &stats);
201 assert!(matches!(j.pop(0, 0, &stats), Pop::Wait));
202 j.push(1, false, &frame(1), 0, &stats);
203 assert!(matches!(j.pop(0, 0, &stats), Pop::Frame(p) if p == frame(0)));
204 assert!(matches!(j.pop(0, 0, &stats), Pop::Frame(p) if p == frame(1)));
205 j.push(3, false, &frame(3), 0, &stats);
208 assert!(matches!(j.pop(0, 0, &stats), Pop::Conceal));
209 assert!(matches!(j.pop(1, 0, &stats), Pop::Fec(p) if p == frame(3)));
210 assert!(matches!(j.pop(0, 0, &stats), Pop::Frame(p) if p == frame(3)));
211 j.push(2, false, &frame(2), 0, &stats);
213 assert_eq!(stats.late.load(Ordering::Relaxed), 1);
214 assert!(matches!(j.pop(0, 0, &stats), Pop::Conceal));
216 assert_eq!(j.next, Some(4));
217 j.push(4, false, &frame(4), 0, &stats);
218 assert!(matches!(j.pop(1, 0, &stats), Pop::Frame(p) if p == frame(4)));
219 }
220
221 #[test]
222 fn a_stretched_buffer_keeps_its_depth_at_first_then_sheds_to_target() {
223 let stats = MediaStats::default();
224 let mut j = Jitter::default();
225 for s in 0..8u16 {
226 j.push(s, false, &frame(s as u8), 0, &stats);
227 }
228 j.jitter_ms = 0.0;
230 j.peak_ms = 0.0;
231 assert_eq!(j.target_frames(), PREFILL);
233 assert!(matches!(j.pop(0, 0, &stats), Pop::Frame(p) if p == frame(0)));
234 assert_eq!(stats.shed.load(Ordering::Relaxed), 0);
235 j.shed_after = Some(0);
237 assert!(matches!(j.pop(0, 0, &stats), Pop::Frame(p) if p == frame(6)));
238 assert_eq!(stats.shed.load(Ordering::Relaxed), 5);
239 assert_eq!(stats.lost.load(Ordering::Relaxed), 0);
240 assert_eq!(j.frames.len(), 1);
241 }
242
243 #[test]
244 fn the_target_grows_with_jitter_and_remembers_bursts() {
245 let mut j = Jitter::default();
246 j.jitter_ms = 43.0;
247 assert_eq!(j.target_frames(), PREFILL + 7);
248 j.peak_ms = 300.0;
249 assert_eq!(j.target_frames(), PREFILL + 18);
250 j.jitter_ms = 1000.0;
251 assert_eq!(j.target_frames(), MAX_TARGET);
252 }
253
254 #[test]
255 fn sequence_wraps_and_deep_buffers_jump_forward() {
256 let stats = MediaStats::default();
257 let mut j = Jitter::default();
258 j.push(65534, false, &frame(1), 0, &stats);
259 j.push(65535, false, &frame(2), 0, &stats);
260 j.push(0, false, &frame(3), 0, &stats);
261 assert!(matches!(j.pop(0, 0, &stats), Pop::Frame(p) if p == frame(1)));
262 assert_eq!(j.next, Some(65535));
263 for s in 1..=(MAX_DEPTH as u16 + 2) {
264 j.push(s, false, &frame(9), 0, &stats);
265 }
266 assert!(j.frames.len() <= MAX_DEPTH, "{}", j.frames.len());
267 assert!(stats.lost.load(Ordering::Relaxed) > 0);
268 assert!(j.next.unwrap() > 65537);
270 }
271}