voice_engine/media/
jitter.rs1use crate::media::AudioFrame;
2use std::collections::VecDeque;
3
4#[derive(Debug, Clone)]
5pub struct JitterStats {
6 pub buffer_size: usize,
7 pub total_received: u64,
8 pub total_dropped: u64,
9 pub total_late: u64,
10 pub current_delay: u32,
11}
12
13pub struct JitterBuffer {
14 frames: VecDeque<AudioFrame>,
16 max_size: usize,
17 last_popped_timestamp: Option<u64>,
18
19 total_received: u64,
21 total_dropped: u64,
22 total_late: u64,
23
24 target_delay_ms: u32, max_delay_ms: u32, }
28
29impl JitterBuffer {
30 pub fn new() -> Self {
31 Self::with_max_size(100)
32 }
33
34 pub fn with_max_size(max_size: usize) -> Self {
35 Self::with_config(max_size, 60, 200) }
37
38 pub fn with_config(max_size: usize, target_delay_ms: u32, max_delay_ms: u32) -> Self {
39 Self {
40 frames: VecDeque::new(),
41 max_size,
42 last_popped_timestamp: None,
43 total_received: 0,
44 total_dropped: 0,
45 total_late: 0,
46 target_delay_ms,
47 max_delay_ms,
48 }
49 }
50
51 pub fn push(&mut self, frame: AudioFrame) -> bool {
52 self.total_received += 1;
53
54 if let Some(last_ts) = self.last_popped_timestamp {
56 let ts_diff = frame.timestamp.wrapping_sub(last_ts);
57
58 if ts_diff > (u64::MAX / 2) {
60 self.total_late += 1;
61 return false;
62 }
63
64 if frame.timestamp <= last_ts {
66 self.total_late += 1;
67 return false;
68 }
69 }
70
71 while self.frames.len() >= self.max_size {
73 if let Some(oldest) = self.frames.front() {
74 if frame.timestamp > oldest.timestamp {
75 self.frames.pop_front();
76 self.total_dropped += 1;
77 } else {
78 self.total_late += 1;
80 return false;
81 }
82 } else {
83 break;
84 }
85 }
86
87 let pos = self
89 .frames
90 .binary_search_by_key(&frame.timestamp, |f| f.timestamp)
91 .unwrap_or_else(|pos| pos);
92
93 if pos < self.frames.len() && self.frames[pos].timestamp == frame.timestamp {
95 self.frames[pos] = frame;
96 } else {
97 self.frames.insert(pos, frame);
98 }
99
100 true
101 }
102
103 pub fn pop(&mut self) -> Option<AudioFrame> {
104 if let Some(frame) = self.frames.pop_front() {
105 self.last_popped_timestamp = Some(frame.timestamp);
106 Some(frame)
107 } else {
108 None
109 }
110 }
111
112 pub fn clear(&mut self) {
113 self.frames.clear();
114 self.last_popped_timestamp = None;
115 }
116
117 pub fn len(&self) -> usize {
118 self.frames.len()
119 }
120
121 pub fn is_empty(&self) -> bool {
122 self.frames.is_empty()
123 }
124
125 pub fn pull_frames(&mut self, duration_ms: u32) -> Vec<AudioFrame> {
126 self.pull_frames_with_duration(duration_ms, 20)
127 }
128
129 pub fn pull_frames_with_duration(
131 &mut self,
132 duration_ms: u32,
133 frame_duration_ms: u32,
134 ) -> Vec<AudioFrame> {
135 let mut frames = Vec::new();
136 let frames_to_pull = (duration_ms / frame_duration_ms).max(1) as usize;
137
138 for _ in 0..frames_to_pull {
139 if let Some(frame) = self.pop() {
140 frames.push(frame);
141 } else {
142 break;
143 }
144 }
145 frames
146 }
147
148 pub fn is_ready(&self) -> bool {
150 if self.frames.is_empty() {
151 return false;
152 }
153
154 let now = crate::media::get_timestamp();
155 let oldest_ts = self.frames.front().unwrap().timestamp;
156 let buffer_delay = now.saturating_sub(oldest_ts);
157
158 buffer_delay >= self.target_delay_ms as u64
159 }
160
161 pub fn has_excessive_delay(&self) -> bool {
163 if self.frames.is_empty() {
164 return false;
165 }
166
167 let now = crate::media::get_timestamp();
168 let oldest_ts = self.frames.front().unwrap().timestamp;
169 let buffer_delay = now.saturating_sub(oldest_ts);
170
171 buffer_delay > self.max_delay_ms as u64
172 }
173
174 pub fn stats(&self) -> JitterStats {
176 JitterStats {
177 buffer_size: self.frames.len(),
178 total_received: self.total_received,
179 total_dropped: self.total_dropped,
180 total_late: self.total_late,
181 current_delay: self.current_delay(),
182 }
183 }
184
185 pub fn current_delay(&self) -> u32 {
187 if let Some(oldest) = self.frames.front() {
188 let now = crate::media::get_timestamp();
189 now.saturating_sub(oldest.timestamp) as u32
190 } else {
191 0
192 }
193 }
194
195 pub fn adaptive_cleanup(&mut self) -> usize {
197 let mut removed = 0;
198
199 if self.has_excessive_delay() {
200 let now = crate::media::get_timestamp();
201 let max_age = now.saturating_sub(self.max_delay_ms as u64);
202
203 while let Some(oldest) = self.frames.front() {
204 if oldest.timestamp < max_age {
205 self.frames.pop_front();
206 self.total_dropped += 1;
207 removed += 1;
208 } else {
209 break;
210 }
211 }
212 }
213
214 removed
215 }
216}