pub mod jitter;
pub mod loss;
pub mod reports;
pub mod rtt;
pub use jitter::JitterEstimator;
pub use loss::{PacketLossResult, PacketLossStats, PacketLossTracker};
pub use reports::{RtcpReportGenerator, RTCP_BANDWIDTH_FRACTION, RTCP_MIN_INTERVAL};
pub use rtt::{RttEstimator, RttStats};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use crate::packet::rtcp::NtpTimestamp;
use crate::RtpSequenceNumber;
#[derive(Debug, Clone, Default)]
pub struct RtpStats {
pub packets_sent: u64,
pub bytes_sent: u64,
pub packets_received: u64,
pub bytes_received: u64,
pub packets_lost: u64,
pub fraction_lost: u8,
pub packets_duplicated: u64,
pub packets_out_of_order: u64,
pub jitter: f64,
pub round_trip_time_ms: Option<f64>,
pub last_seq: Option<RtpSequenceNumber>,
pub highest_seq: u32,
pub base_seq: Option<RtpSequenceNumber>,
pub last_sr_timestamp: Option<NtpTimestamp>,
pub delay_since_last_sr_ms: Option<u32>,
}
#[allow(dead_code)] pub struct RtpStatsManager {
stats: Arc<Mutex<RtpStats>>,
jitter_estimator: JitterEstimator,
loss_tracker: PacketLossTracker,
rtt_estimator: RttEstimator,
rtcp_generator: Option<RtcpReportGenerator>,
start_time: Instant,
#[allow(dead_code)] clock_rate: u32,
}
impl RtpStatsManager {
pub fn new(clock_rate: u32) -> Self {
Self {
stats: Arc::new(Mutex::new(RtpStats::default())),
jitter_estimator: JitterEstimator::new(clock_rate),
loss_tracker: PacketLossTracker::new(),
rtt_estimator: RttEstimator::new(),
rtcp_generator: None,
start_time: Instant::now(),
clock_rate,
}
}
pub fn new_with_rtcp(clock_rate: u32, local_ssrc: u32, cname: String) -> Self {
let mut manager = Self::new(clock_rate);
manager.rtcp_generator = Some(RtcpReportGenerator::new(local_ssrc, cname));
manager
}
pub fn get_stats(&self) -> RtpStats {
self.stats.lock().unwrap().clone()
}
pub fn reset(&mut self) {
*self.stats.lock().unwrap() = RtpStats::default();
self.jitter_estimator.reset();
self.loss_tracker.reset();
self.rtt_estimator.reset();
self.start_time = Instant::now();
}
pub fn duration(&self) -> Duration {
self.start_time.elapsed()
}
pub fn update_sent(&mut self, bytes: usize) {
let mut stats = self.stats.lock().unwrap();
stats.packets_sent += 1;
stats.bytes_sent += bytes as u64;
if let Some(generator) = &mut self.rtcp_generator {
generator.update_sent_stats(1, bytes as u32);
}
}
pub fn update_received(
&mut self,
seq: RtpSequenceNumber,
timestamp: u32,
bytes: usize,
arrival_time: Instant,
) {
let mut stats = self.stats.lock().unwrap();
stats.packets_received += 1;
stats.bytes_received += bytes as u64;
let result = self.loss_tracker.process(seq);
match result {
PacketLossResult::FirstPacket { seq } => {
stats.base_seq = Some(seq);
stats.highest_seq = seq as u32;
stats.last_seq = Some(seq);
}
PacketLossResult::Sequential { seq } => {
stats.highest_seq = seq as u32;
stats.last_seq = Some(seq);
}
PacketLossResult::Gap {
seq,
expected: _,
lost,
} => {
stats.packets_lost += lost as u64;
stats.highest_seq = seq as u32;
stats.last_seq = Some(seq);
}
PacketLossResult::Duplicate { seq: _ } => {
stats.packets_duplicated += 1;
}
PacketLossResult::Reordered { seq, expected: _ } => {
stats.packets_out_of_order += 1;
stats.last_seq = Some(seq);
}
PacketLossResult::Unknown => {}
}
let jitter = self.jitter_estimator.update(timestamp, arrival_time);
stats.jitter = jitter;
let loss_stats = self.loss_tracker.get_stats();
stats.fraction_lost = loss_stats.fraction_lost;
if let Some(generator) = &mut self.rtcp_generator {
generator.process_received_packet(0, seq); }
}
pub fn update_rtt(&self, rtt_ms: f64) {
let mut stats = self.stats.lock().unwrap();
stats.round_trip_time_ms = Some(rtt_ms);
}
pub fn update_sr_info(&self, last_sr: NtpTimestamp, delay_ms: u32) {
let mut stats = self.stats.lock().unwrap();
stats.last_sr_timestamp = Some(last_sr);
stats.delay_since_last_sr_ms = Some(delay_ms);
}
pub fn rtcp_generator(&mut self) -> Option<&mut RtcpReportGenerator> {
self.rtcp_generator.as_mut()
}
pub fn jitter_estimator(&self) -> &JitterEstimator {
&self.jitter_estimator
}
pub fn loss_tracker(&self) -> &PacketLossTracker {
&self.loss_tracker
}
pub fn rtt_estimator(&self) -> &RttEstimator {
&self.rtt_estimator
}
}
impl Default for RtpStatsManager {
fn default() -> Self {
Self::new(8000) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stats_manager() {
let mut manager = RtpStatsManager::new(8000);
let stats = manager.get_stats();
assert_eq!(stats.packets_sent, 0);
assert_eq!(stats.packets_received, 0);
assert_eq!(stats.packets_lost, 0);
assert_eq!(stats.packets_duplicated, 0);
assert_eq!(stats.packets_out_of_order, 0);
assert!(stats.last_seq.is_none());
manager.update_sent(100);
let stats = manager.get_stats();
assert_eq!(stats.packets_sent, 1);
assert_eq!(stats.bytes_sent, 100);
}
}