voice_engine/media/
jitter.rs

1use 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    // Use VecDeque for better memory efficiency
15    frames: VecDeque<AudioFrame>,
16    max_size: usize,
17    last_popped_timestamp: Option<u64>,
18
19    // Add statistics
20    total_received: u64,
21    total_dropped: u64,
22    total_late: u64,
23
24    // Buffer configuration
25    target_delay_ms: u32, // Target buffering delay
26    max_delay_ms: u32,    // Maximum acceptable delay
27}
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) // 60ms target, 200ms max
36    }
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        // Handle timestamp wraparound and reject very old frames
55        if let Some(last_ts) = self.last_popped_timestamp {
56            let ts_diff = frame.timestamp.wrapping_sub(last_ts);
57
58            // Reject very old frames (handle wraparound)
59            if ts_diff > (u64::MAX / 2) {
60                self.total_late += 1;
61                return false;
62            }
63
64            // Don't add frames with timestamps earlier than the last popped timestamp
65            if frame.timestamp <= last_ts {
66                self.total_late += 1;
67                return false;
68            }
69        }
70
71        // Maintain buffer size limit
72        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                    // New frame is older than our oldest frame, don't add it
79                    self.total_late += 1;
80                    return false;
81                }
82            } else {
83                break;
84            }
85        }
86
87        // Insert in sorted order
88        let pos = self
89            .frames
90            .binary_search_by_key(&frame.timestamp, |f| f.timestamp)
91            .unwrap_or_else(|pos| pos);
92
93        // Handle duplicate timestamps by replacing
94        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    // Improved pull_frames with configurable frame duration
130    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    // New: Check if ready to output (has enough buffer)
149    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    // New: Check if buffer has excessive delay
162    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    // New: Get buffer statistics
175    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    // New: Get current buffer delay
186    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    // New: Adaptive buffer management - remove old frames if delay is excessive
196    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}