use crate::packet::RtpPacket;
use crate::RtpSequenceNumber;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::Notify;
use tracing::{debug, trace};
use super::GlobalBufferManager;
pub const DEFAULT_JITTER_BUFFER_SIZE_MS: u32 = 50;
pub const MIN_JITTER_BUFFER_SIZE_MS: u32 = 10;
pub const MAX_JITTER_BUFFER_SIZE_MS: u32 = 500;
pub const DEFAULT_MAX_OUT_OF_ORDER: usize = 100;
pub const DEFAULT_PLAYOUT_DELAY_MS: u32 = 60;
#[derive(Debug, Clone)]
pub struct JitterBufferConfig {
pub initial_size_ms: u32,
pub min_size_ms: u32,
pub max_size_ms: u32,
pub clock_rate: u32,
pub max_out_of_order: usize,
pub max_packet_age_ms: u32,
pub initial_playout_delay_ms: u32,
pub adaptive: bool,
}
impl Default for JitterBufferConfig {
fn default() -> Self {
Self {
initial_size_ms: DEFAULT_JITTER_BUFFER_SIZE_MS,
min_size_ms: MIN_JITTER_BUFFER_SIZE_MS,
max_size_ms: MAX_JITTER_BUFFER_SIZE_MS,
clock_rate: 8000, max_out_of_order: DEFAULT_MAX_OUT_OF_ORDER,
max_packet_age_ms: 200,
initial_playout_delay_ms: DEFAULT_PLAYOUT_DELAY_MS,
adaptive: true,
}
}
}
#[derive(Debug, Clone)]
pub struct JitterBufferStats {
pub buffer_size_ms: u32,
pub buffered_packets: usize,
pub packets_received: u64,
pub packets_played: u64,
pub packets_too_late: u64,
pub packets_overflow: u64,
pub duplicates: u64,
pub jitter_ms: f64,
pub discontinuities: u64,
pub underruns: u64,
pub avg_delay_ms: f64,
}
#[allow(dead_code)] pub struct AdaptiveJitterBuffer {
config: JitterBufferConfig,
packets: BTreeMap<u32, (RtpPacket, Instant)>,
next_seq: Option<RtpSequenceNumber>,
ext_seq_base: u32,
seq_cycles: u16,
highest_seq: u32,
jitter: f64,
buffer_size_ms: u32,
last_playout_time: Option<Instant>,
last_timestamp: Option<u32>,
stats: JitterBufferStats,
#[allow(dead_code)] start_time: Instant,
buffer_manager: Option<Arc<GlobalBufferManager>>,
packet_notify: Arc<Notify>,
last_adjustment: Instant,
arrival_jitter: f64,
avg_delay: f64,
waiting_packets: u32,
initial_seq: Option<RtpSequenceNumber>,
playout_delay: u32,
}
impl AdaptiveJitterBuffer {
pub fn new(config: JitterBufferConfig) -> Self {
let now = Instant::now();
let playout_delay =
((config.initial_playout_delay_ms as f64 / 1000.0) * config.clock_rate as f64) as u32;
let initial_size_ms = config.initial_size_ms;
let stats = JitterBufferStats {
buffer_size_ms: initial_size_ms,
buffered_packets: 0,
packets_received: 0,
packets_played: 0,
packets_too_late: 0,
packets_overflow: 0,
duplicates: 0,
jitter_ms: 0.0,
discontinuities: 0,
underruns: 0,
avg_delay_ms: 0.0,
};
Self {
config,
packets: BTreeMap::new(),
next_seq: None,
ext_seq_base: 0,
seq_cycles: 0,
highest_seq: 0,
jitter: 0.0,
buffer_size_ms: initial_size_ms,
last_playout_time: None,
last_timestamp: None,
stats,
start_time: now,
buffer_manager: None,
packet_notify: Arc::new(Notify::new()),
last_adjustment: now,
arrival_jitter: 0.0,
avg_delay: 0.0,
waiting_packets: 0,
initial_seq: None,
playout_delay,
}
}
pub fn with_buffer_manager(
config: JitterBufferConfig,
buffer_manager: Arc<GlobalBufferManager>,
) -> Self {
let mut buffer = Self::new(config);
buffer.buffer_manager = Some(buffer_manager);
buffer
}
pub fn add_packet(&mut self, packet: RtpPacket) -> bool {
let now = Instant::now();
let seq = packet.header.sequence_number;
let ts = packet.header.timestamp;
trace!("Adding packet with seq={} to jitter buffer", seq);
self.stats.packets_received += 1;
if self.next_seq.is_none() {
self.initial_seq = Some(seq);
self.next_seq = Some(seq);
self.highest_seq = seq as u32;
trace!("First packet in buffer, initializing with seq={}", seq);
self.packets.insert(seq as u32, (packet, now));
self.stats.buffered_packets = self.packets.len();
self.packet_notify.notify_one();
return true;
}
if self.packets.contains_key(&(seq as u32)) {
self.stats.duplicates += 1;
trace!("Duplicate packet detected with seq={}", seq);
return false;
}
if let Some(next_seq) = self.next_seq {
let is_late = if next_seq > seq {
let diff = if next_seq - seq < 0x8000 {
next_seq - seq
} else {
0
};
usize::from(diff) > self.config.max_out_of_order
} else {
false
};
if is_late {
trace!(
"Packet too late: seq={}, next_seq={}, diff={}",
seq,
next_seq,
if next_seq > seq { next_seq - seq } else { 0 }
);
self.stats.packets_too_late += 1;
return false;
}
}
let highest_seq = self.highest_seq as u16;
if seq.wrapping_sub(highest_seq) < 0x8000 {
if self.seq_cycles > 0 && seq < 0x1000 && highest_seq > 0xf000 {
debug!("Sequence wraparound detected: {} -> {}", highest_seq, seq);
self.seq_cycles += 1;
}
self.highest_seq = ((self.seq_cycles as u32) << 16) | (seq as u32);
trace!("Updated highest sequence to {}", seq);
}
if self.packets.len() >= self.config.max_out_of_order {
trace!(
"Buffer overflow (max_size={}), dropping oldest packet",
self.config.max_out_of_order
);
self.stats.packets_overflow += 1;
if let Some((&oldest_seq, _)) = self.packets.iter().next() {
self.packets.remove(&oldest_seq);
}
}
if let Some(last_time) = self.last_playout_time {
if let Some(last_ts) = self.last_timestamp {
let arrival_diff = now.duration_since(last_time).as_secs_f64();
let ts_diff =
((ts as i32 - last_ts as i32).abs() as f64) / (self.config.clock_rate as f64);
let d = (arrival_diff - ts_diff).abs();
self.jitter += (d - self.jitter) / 16.0;
self.arrival_jitter = self.jitter;
self.avg_delay = 0.8 * self.avg_delay + 0.2 * d;
}
}
self.packets.insert(seq as u32, (packet, now));
self.stats.buffered_packets = self.packets.len();
self.last_playout_time = Some(now);
self.last_timestamp = Some(ts);
self.packet_notify.notify_one();
if self.config.adaptive {
self.maybe_adapt_buffer_size(now);
}
true
}
pub fn get_next_packet(&mut self) -> Option<RtpPacket> {
if self.packets.is_empty() {
return None;
}
let lowest_seq = self.packets.keys().copied().min();
if let Some(lowest) = lowest_seq {
if self.next_seq.is_none() {
self.next_seq = Some(lowest as u16);
} else if lowest < self.next_seq.unwrap() as u32 {
let next_seq = self.next_seq.unwrap();
if !(lowest < 1000 && next_seq > 60000) {
self.next_seq = Some(lowest as u16);
}
}
} else {
return None;
}
let next_seq = self.next_seq.unwrap();
let next_seq_u32 = next_seq as u32;
trace!("Getting next packet, expecting seq={}", next_seq);
if let Some((packet, arrival_time)) = self.packets.remove(&next_seq_u32) {
self.next_seq = Some(next_seq.wrapping_add(1));
trace!(
"Found expected packet seq={}, next_seq now={}",
next_seq,
next_seq.wrapping_add(1)
);
self.stats.packets_played += 1;
self.stats.buffered_packets = self.packets.len();
let buffered_time = Instant::now().duration_since(arrival_time).as_millis() as u32;
trace!("Packet played after {}ms in buffer", buffered_time);
self.avg_delay = 0.8 * self.avg_delay + 0.2 * (buffered_time as f64 / 1000.0);
self.stats.avg_delay_ms = self.avg_delay * 1000.0;
return Some(packet);
}
trace!(
"Packet with seq={} not found in buffer, handling discontinuity",
next_seq
);
self.stats.discontinuities += 1;
let mut keys: Vec<_> = self.packets.keys().copied().collect();
if keys.is_empty() {
self.stats.underruns += 1;
debug!("Buffer underrun, no packets available");
return None;
}
keys.sort();
let next_available = keys
.iter()
.min_by(|&&a, &&b| {
let dist_a = ((a as u16).wrapping_sub(next_seq) as u32) & 0xFFFF;
let dist_b = ((b as u16).wrapping_sub(next_seq) as u32) & 0xFFFF;
dist_a.cmp(&dist_b)
})
.copied()
.unwrap_or(keys[0]);
trace!("Skipping to packet with seq={}", next_available as u16);
self.next_seq = Some((next_available as u16).wrapping_add(1));
debug!(
"Handling packet loss, skipping to seq={}",
next_available as u16
);
let (packet, arrival_time) = self.packets.remove(&next_available).unwrap();
self.stats.packets_played += 1;
self.stats.buffered_packets = self.packets.len();
let buffered_time = Instant::now().duration_since(arrival_time).as_millis() as u32;
trace!("Packet played after {}ms in buffer", buffered_time);
Some(packet)
}
pub async fn wait_for_packet(&self, timeout: Duration) -> bool {
if !self.packets.is_empty() {
return true;
}
let notify = self.packet_notify.clone();
tokio::select! {
_ = notify.notified() => true,
_ = tokio::time::sleep(timeout) => false,
}
}
fn maybe_adapt_buffer_size(&mut self, now: Instant) {
if now.duration_since(self.last_adjustment).as_millis() < 1000 {
return;
}
self.last_adjustment = now;
let jitter_ms = self.jitter * 1000.0;
self.stats.jitter_ms = jitter_ms;
let desired_ms = (jitter_ms * 4.0) as u32;
let new_size = desired_ms
.max(self.config.min_size_ms)
.min(self.config.max_size_ms);
if (new_size as i32 - self.buffer_size_ms as i32).abs() > 10 {
debug!(
"Adapting jitter buffer size: {}ms -> {}ms (jitter: {:.2}ms)",
self.buffer_size_ms, new_size, jitter_ms
);
self.buffer_size_ms = new_size;
self.stats.buffer_size_ms = new_size;
self.playout_delay =
((new_size as f64 / 1000.0) * self.config.clock_rate as f64) as u32;
}
}
pub fn get_stats(&self) -> JitterBufferStats {
self.stats.clone()
}
pub fn get_jitter_ms(&self) -> f64 {
self.jitter * 1000.0
}
pub fn clear(&mut self) {
self.packets.clear();
self.stats.buffered_packets = 0;
}
pub fn reset(&mut self) {
self.clear();
self.next_seq = None;
self.ext_seq_base = 0;
self.seq_cycles = 0;
self.highest_seq = 0;
self.jitter = 0.0;
self.last_playout_time = None;
self.last_timestamp = None;
self.arrival_jitter = 0.0;
self.avg_delay = 0.0;
self.waiting_packets = 0;
self.initial_seq = None;
self.stats = JitterBufferStats {
buffer_size_ms: self.config.initial_size_ms,
buffered_packets: 0,
packets_received: 0,
packets_played: 0,
packets_too_late: 0,
packets_overflow: 0,
duplicates: 0,
jitter_ms: 0.0,
discontinuities: 0,
underruns: 0,
avg_delay_ms: 0.0,
};
self.buffer_size_ms = self.config.initial_size_ms;
self.playout_delay = ((self.config.initial_playout_delay_ms as f64 / 1000.0)
* self.config.clock_rate as f64) as u32;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packet::{RtpHeader, RtpPacket};
use bytes::Bytes;
fn create_test_packet(seq: RtpSequenceNumber, ts: u32) -> RtpPacket {
let header = RtpHeader::new(96, seq, ts, 0x12345678);
let payload = Bytes::from_static(b"test");
RtpPacket::new(header, payload)
}
#[tokio::test]
async fn test_in_order_packets() {
let config = JitterBufferConfig {
initial_size_ms: 50,
clock_rate: 8000,
..Default::default()
};
let mut jitter = AdaptiveJitterBuffer::new(config);
assert!(jitter.add_packet(create_test_packet(1, 0)));
assert!(jitter.add_packet(create_test_packet(2, 160)));
assert!(jitter.add_packet(create_test_packet(3, 320)));
let p1 = jitter.get_next_packet();
assert!(p1.is_some(), "First packet should be available");
assert_eq!(
p1.unwrap().header.sequence_number,
1,
"First packet should have seq=1"
);
let p2 = jitter.get_next_packet();
assert!(p2.is_some(), "Second packet should be available");
assert_eq!(
p2.unwrap().header.sequence_number,
2,
"Second packet should have seq=2"
);
let p3 = jitter.get_next_packet();
assert!(p3.is_some(), "Third packet should be available");
assert_eq!(
p3.unwrap().header.sequence_number,
3,
"Third packet should have seq=3"
);
assert!(
jitter.get_next_packet().is_none(),
"Buffer should be empty after reading all packets"
);
}
#[tokio::test]
async fn test_out_of_order_packets() {
let config = JitterBufferConfig {
initial_size_ms: 50,
clock_rate: 8000,
..Default::default()
};
let mut jitter = AdaptiveJitterBuffer::new(config);
assert!(jitter.add_packet(create_test_packet(2, 160)));
assert!(
jitter.add_packet(create_test_packet(1, 0)),
"Jitter buffer should accept out of order packets"
);
assert!(jitter.add_packet(create_test_packet(3, 320)));
let stats = jitter.get_stats();
assert_eq!(stats.buffered_packets, 3, "Buffer should contain 3 packets");
let p1 = jitter.get_next_packet();
assert!(p1.is_some(), "First packet should be available");
assert_eq!(
p1.unwrap().header.sequence_number,
1,
"First packet should have seq=1"
);
let p2 = jitter.get_next_packet();
assert!(p2.is_some(), "Second packet should be available");
assert_eq!(
p2.unwrap().header.sequence_number,
2,
"Second packet should have seq=2"
);
let p3 = jitter.get_next_packet();
assert!(p3.is_some(), "Third packet should be available");
assert_eq!(
p3.unwrap().header.sequence_number,
3,
"Third packet should have seq=3"
);
}
#[tokio::test]
async fn test_packet_loss() {
let config = JitterBufferConfig {
initial_size_ms: 50,
clock_rate: 8000,
..Default::default()
};
let mut jitter = AdaptiveJitterBuffer::new(config);
assert!(jitter.add_packet(create_test_packet(1, 0)));
assert!(jitter.add_packet(create_test_packet(2, 160)));
assert!(jitter.add_packet(create_test_packet(4, 480)));
let stats = jitter.get_stats();
assert_eq!(stats.buffered_packets, 3, "Buffer should contain 3 packets");
let p1 = jitter.get_next_packet();
assert!(p1.is_some(), "First packet should be available");
assert_eq!(
p1.unwrap().header.sequence_number,
1,
"First packet should have seq=1"
);
let p2 = jitter.get_next_packet();
assert!(p2.is_some(), "Second packet should be available");
assert_eq!(
p2.unwrap().header.sequence_number,
2,
"Second packet should have seq=2"
);
let p3 = jitter.get_next_packet();
assert!(p3.is_some(), "Third packet should be available");
assert_eq!(
p3.unwrap().header.sequence_number,
4,
"Third packet should have seq=4"
);
let stats = jitter.get_stats();
assert_eq!(
stats.discontinuities, 1,
"Should detect one sequence discontinuity"
);
}
#[tokio::test]
async fn test_sequence_wraparound() {
let config = JitterBufferConfig {
initial_size_ms: 50,
clock_rate: 8000,
..Default::default()
};
let mut jitter = AdaptiveJitterBuffer::new(config);
assert!(jitter.add_packet(create_test_packet(65534, 10000)));
assert!(jitter.add_packet(create_test_packet(65535, 10160)));
assert!(jitter.add_packet(create_test_packet(0, 10320)));
assert!(jitter.add_packet(create_test_packet(1, 10480)));
let stats = jitter.get_stats();
assert_eq!(stats.buffered_packets, 4, "Buffer should contain 4 packets");
let p1 = jitter.get_next_packet();
assert!(p1.is_some(), "First packet should be available");
assert_eq!(
p1.unwrap().header.sequence_number,
65534,
"First packet should have seq=65534"
);
let p2 = jitter.get_next_packet();
assert!(p2.is_some(), "Second packet should be available");
assert_eq!(
p2.unwrap().header.sequence_number,
65535,
"Second packet should have seq=65535"
);
let p3 = jitter.get_next_packet();
assert!(p3.is_some(), "Third packet should be available");
assert_eq!(
p3.unwrap().header.sequence_number,
0,
"Third packet should have seq=0"
);
let p4 = jitter.get_next_packet();
assert!(p4.is_some(), "Fourth packet should be available");
assert_eq!(
p4.unwrap().header.sequence_number,
1,
"Fourth packet should have seq=1"
);
}
}