use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use tracing::{debug, warn};
use crate::packet::RtpPacket;
use crate::{RtpSequenceNumber, RtpSsrc, RtpTimestamp};
pub struct RtpStream {
pub ssrc: RtpSsrc,
latest_seq: RtpSequenceNumber,
highest_seq: u32,
base_seq: RtpSequenceNumber,
initialized: bool,
last_packet_time: Instant,
packets_received: u64,
bytes_received: u64,
packets_lost: u64,
duplicates: u64,
jitter: f64,
last_arrival: Option<Instant>,
last_timestamp: Option<RtpTimestamp>,
clock_rate: u32,
jitter_buffer: Option<Arc<Mutex<VecDeque<RtpPacket>>>>,
max_jitter_size: usize,
max_packet_age: Duration,
seq_cycles: u16,
last_sr_timestamp: Option<u32>,
last_sr_time: Option<Instant>,
#[cfg(feature = "memory-diagnostics")]
_memory_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard,
}
impl RtpStream {
pub fn new(ssrc: RtpSsrc, clock_rate: u32) -> Self {
Self {
ssrc,
latest_seq: 0,
highest_seq: 0,
base_seq: 0,
initialized: false,
last_packet_time: Instant::now(),
packets_received: 0,
bytes_received: 0,
packets_lost: 0,
duplicates: 0,
jitter: 0.0,
last_arrival: None,
last_timestamp: None,
clock_rate,
jitter_buffer: None,
max_jitter_size: 50, max_packet_age: Duration::from_millis(200), seq_cycles: 0,
last_sr_timestamp: None,
last_sr_time: None,
#[cfg(feature = "memory-diagnostics")]
_memory_guard: rvoip_infra_common::memory_diagnostics::ObjectGuard::new(
"rtp_core.rtp_stream",
std::mem::size_of::<Self>(),
),
}
}
pub fn with_jitter_buffer(
ssrc: RtpSsrc,
clock_rate: u32,
buffer_size: usize,
max_age_ms: u64,
) -> Self {
let mut stream = Self::new(ssrc, clock_rate);
stream.enable_jitter_buffer(buffer_size, max_age_ms);
stream
}
pub fn enable_jitter_buffer(&mut self, size: usize, max_age_ms: u64) {
self.jitter_buffer = Some(Arc::new(Mutex::new(VecDeque::with_capacity(size))));
self.max_jitter_size = size;
self.max_packet_age = Duration::from_millis(max_age_ms);
}
pub fn disable_jitter_buffer(&mut self) {
self.jitter_buffer = None;
}
pub fn process_packet(&mut self, packet: RtpPacket) -> Option<RtpPacket> {
let now = Instant::now();
self.last_packet_time = now;
let seq = packet.header.sequence_number;
let timestamp = packet.header.timestamp;
self.packets_received += 1;
self.bytes_received += packet.size() as u64;
if !self.initialized {
self.init_sequence(seq);
self.last_timestamp = Some(timestamp);
self.last_arrival = Some(now);
self.initialized = true;
return Some(packet);
}
self.update_sequence(seq);
if let (Some(last_arrival), Some(last_ts)) = (self.last_arrival, self.last_timestamp) {
let arrival_diff = now.duration_since(last_arrival).as_secs_f64();
let ts_diff =
((timestamp as i32 - last_ts as i32).abs() as f64) / (self.clock_rate as f64);
let d = arrival_diff - ts_diff;
self.jitter += (d.abs() - self.jitter) / 16.0;
}
self.last_arrival = Some(now);
self.last_timestamp = Some(timestamp);
if let Some(buffer) = &self.jitter_buffer {
self.add_to_jitter_buffer(packet, buffer.clone());
self.get_next_from_jitter_buffer(buffer.clone())
} else {
Some(packet)
}
}
fn init_sequence(&mut self, seq: RtpSequenceNumber) {
self.base_seq = seq;
self.latest_seq = seq;
self.highest_seq = seq as u32;
debug!("Initialized RTP stream with seq={}", seq);
}
fn update_sequence(&mut self, seq: RtpSequenceNumber) {
if seq < 0x1000 && self.latest_seq > 0xf000 {
debug!(
"Detected sequence wraparound: {} -> {}",
self.latest_seq, seq
);
self.seq_cycles += 1;
}
if seq == self.latest_seq {
self.duplicates += 1;
return;
}
let extended_seq = (self.seq_cycles as u32) << 16 | (seq as u32);
if extended_seq > self.highest_seq {
let expected_seq = (self.latest_seq as u32 + 1) & 0xFFFF;
if seq != expected_seq as u16 {
let gap = if seq > expected_seq as u16 {
seq - expected_seq as u16
} else {
((0xFFFF as u32 + 1) - expected_seq as u32) as u16 + seq
};
if gap > 0 {
self.packets_lost += gap as u64;
debug!(
"Detected sequence gap: expected={}, got={}, lost={}",
expected_seq, seq, gap
);
}
}
self.highest_seq = extended_seq;
} else {
debug!(
"Out of order packet: seq={}, highest={}",
seq,
self.highest_seq & 0xFFFF
);
}
self.latest_seq = seq;
}
fn add_to_jitter_buffer(&self, packet: RtpPacket, buffer: Arc<Mutex<VecDeque<RtpPacket>>>) {
if let Ok(mut buffer_lock) = buffer.lock() {
if buffer_lock.len() >= self.max_jitter_size {
buffer_lock.pop_front();
warn!("Jitter buffer full, dropping oldest packet");
}
let seq = packet.header.sequence_number;
let pos = buffer_lock.iter().position(|p| {
let p_seq = p.header.sequence_number;
is_sequence_newer(seq, p_seq)
});
if let Some(pos) = pos {
buffer_lock.insert(pos, packet);
} else {
buffer_lock.push_back(packet);
}
}
}
fn get_next_from_jitter_buffer(
&self,
buffer: Arc<Mutex<VecDeque<RtpPacket>>>,
) -> Option<RtpPacket> {
if let Ok(mut buffer_lock) = buffer.lock() {
if buffer_lock.is_empty() {
return None;
}
let first_packet = buffer_lock.front()?;
let expected_seq = (self.latest_seq as u32 + 1) & 0xFFFF;
if first_packet.header.sequence_number == expected_seq as u16 {
return buffer_lock.pop_front();
}
if buffer_lock.len() > self.max_jitter_size / 2 {
return buffer_lock.pop_front();
}
}
None
}
pub fn get_jitter_ms(&self) -> f64 {
self.jitter * 1000.0
}
pub fn get_stats(&self) -> RtpStreamStats {
RtpStreamStats {
ssrc: self.ssrc,
packets_received: self.packets_received,
bytes_received: self.bytes_received,
packets_lost: self.packets_lost,
duplicates: self.duplicates,
last_packet_time: Some(self.last_packet_time),
jitter: self.jitter as u32,
first_seq: self.base_seq as u32,
highest_seq: self.highest_seq,
received: self.packets_received as u32,
}
}
pub fn ensure_initialized(&mut self, seq: u16) {
if !self.initialized {
self.init_sequence(seq as RtpSequenceNumber);
self.packets_received = 0;
self.highest_seq = seq as u32;
self.latest_seq = seq as RtpSequenceNumber;
self.packets_lost = 0;
self.duplicates = 0;
self.jitter = 0.0;
self.last_arrival = None;
self.last_timestamp = None;
self.initialized = true;
debug!("Initialized RTP stream with seq={}", seq);
}
}
pub fn update_last_sr_info(&mut self, sr_timestamp: u32, time: Instant) {
self.last_sr_timestamp = Some(sr_timestamp);
self.last_sr_time = Some(time);
}
pub fn get_last_sr_info(&self) -> (Option<u32>, Option<Instant>) {
(self.last_sr_timestamp, self.last_sr_time)
}
pub fn calculate_delay_since_last_sr(&self) -> u32 {
if let (Some(_timestamp), Some(time)) = (self.last_sr_timestamp, self.last_sr_time) {
let delay_secs = Instant::now().duration_since(time).as_secs_f64();
(delay_secs * 65536.0) as u32
} else {
0
}
}
}
fn is_sequence_newer(a: RtpSequenceNumber, b: RtpSequenceNumber) -> bool {
let half_range = 0x8000;
if b < a {
(a - b) <= half_range
} else {
(b - a) > half_range
}
}
#[derive(Debug, Clone, Default)]
pub struct RtpStreamStats {
pub ssrc: RtpSsrc,
pub packets_received: u64,
pub bytes_received: u64,
pub packets_lost: u64,
pub duplicates: u64,
pub last_packet_time: Option<Instant>,
pub jitter: u32,
pub first_seq: u32,
pub highest_seq: u32,
pub received: u32,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packet::RtpHeader;
use bytes::Bytes;
fn create_test_packet(seq: RtpSequenceNumber, ts: RtpTimestamp) -> RtpPacket {
let header = RtpHeader::new(96, seq, ts, 0x12345678);
let payload = Bytes::from_static(b"test");
RtpPacket::new(header, payload)
}
#[test]
fn test_sequence_tracking() {
let mut stream = RtpStream::new(0x12345678, 8000);
stream.process_packet(create_test_packet(1000, 80000));
assert_eq!(stream.base_seq, 1000);
assert_eq!(stream.highest_seq, 1000);
assert_eq!(stream.packets_received, 1);
assert_eq!(stream.packets_lost, 0);
stream.process_packet(create_test_packet(1001, 80160));
assert_eq!(stream.highest_seq, 1001);
assert_eq!(stream.packets_received, 2);
assert_eq!(stream.packets_lost, 0);
stream.process_packet(create_test_packet(1005, 80800));
assert_eq!(stream.highest_seq, 1005);
assert_eq!(stream.packets_received, 3);
assert_eq!(stream.packets_lost, 3);
stream.process_packet(create_test_packet(1005, 80800));
assert_eq!(stream.highest_seq, 1005);
assert_eq!(stream.packets_received, 4);
assert_eq!(stream.duplicates, 1);
stream.process_packet(create_test_packet(1003, 80480));
assert_eq!(stream.highest_seq, 1005); assert_eq!(stream.packets_received, 5);
assert_eq!(stream.packets_lost, 3); }
#[test]
fn test_sequence_wraparound() {
let mut stream = RtpStream::new(0x12345678, 8000);
stream.process_packet(create_test_packet(65530, 80000));
assert_eq!(stream.base_seq, 65530);
assert_eq!(stream.highest_seq, 65530);
assert_eq!(stream.seq_cycles, 0);
stream.process_packet(create_test_packet(65531, 80160));
stream.process_packet(create_test_packet(65532, 80320));
stream.process_packet(create_test_packet(65533, 80480));
stream.process_packet(create_test_packet(65534, 80640));
stream.process_packet(create_test_packet(65535, 80800));
assert_eq!(stream.highest_seq, 65535);
assert_eq!(stream.seq_cycles, 0);
stream.process_packet(create_test_packet(0, 80960));
assert_eq!(stream.highest_seq, 65536); assert_eq!(stream.seq_cycles, 1);
stream.process_packet(create_test_packet(1, 81120));
stream.process_packet(create_test_packet(2, 81280));
assert_eq!(stream.highest_seq, 65538); assert_eq!(stream.seq_cycles, 1);
}
#[test]
fn test_is_sequence_newer() {
assert!(is_sequence_newer(101, 100));
assert!(!is_sequence_newer(100, 101));
assert!(!is_sequence_newer(100, 100));
assert!(is_sequence_newer(0, 65535));
assert!(!is_sequence_newer(65535, 0));
assert!(is_sequence_newer(1, 65000));
assert!(!is_sequence_newer(65000, 1));
assert!(is_sequence_newer(32768, 0));
assert!(!is_sequence_newer(0, 32768));
}
}