use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::utils::net_utils::IntervalResult;
pub(crate) const HEADER_SIZE: usize = 8 + 8 + 4 + 4;
pub(crate) const FLAG_DATA: u32 = 0;
pub(crate) const FLAG_FIN: u32 = 1;
pub(crate) struct UdpHeader {
seq: u64, sec: u64, usec: u32, pub flags: u32, }
const ACCEPTABLE: u32 = 99;
const ACCEPTABLEDECIMAL: u32 = 98;
impl UdpHeader {
pub(crate) fn new(seq: u64, sec: u64, usec: u32, flag: u32) -> Self {
Self {
seq: seq,
sec: sec,
usec: usec,
flags: flag,
}
}
pub(crate) fn write_header(&mut self, buffer: &mut [u8]) {
assert!(buffer.len() >= HEADER_SIZE);
buffer[0..8].copy_from_slice(&self.seq.to_be_bytes());
buffer[8..16].copy_from_slice(&self.sec.to_be_bytes());
buffer[16..20].copy_from_slice(&self.usec.to_be_bytes());
buffer[20..24].copy_from_slice(&self.flags.to_be_bytes());
}
pub(crate) fn read_header(buffer: &mut [u8]) -> Self {
let seq = u64::from_be_bytes(buffer[0..8].try_into().unwrap());
let sec = u64::from_be_bytes(buffer[8..16].try_into().unwrap());
let usec = u32::from_be_bytes(buffer[16..20].try_into().unwrap());
let flags = u32::from_be_bytes(buffer[20..24].try_into().unwrap());
Self {
seq,
sec,
usec,
flags,
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct UdpData {
last_seq: Option<u64>,
interval_result: IntervalResult,
prev_transit_ms: Option<f64>,
pub recommend_pps: f64,
}
impl UdpData {
pub(crate) fn new() -> Self {
Self {
last_seq: None,
interval_result: IntervalResult::default(),
prev_transit_ms: None,
recommend_pps: 0.0,
}
}
pub(crate) fn process_packet(
&mut self,
packet_len: usize,
h: &UdpHeader,
now_since_start: Duration,
) {
self.interval_result.received += 1;
self.interval_result.bytes += packet_len;
match self.last_seq {
None => self.last_seq = Some(h.seq),
Some(prev) => {
if h.seq == prev {
} else if h.seq == prev + 1 {
self.last_seq = Some(h.seq);
} else if h.seq > (prev + 1) {
self.interval_result.lost = h.seq - (prev + 1);
self.last_seq = Some(h.seq);
} else {
self.interval_result.out_of_order += 1;
}
}
}
let send_ms = (h.sec as f64) * 1000.0 + (h.usec as f64) / 1000.0;
let arrival_ms = now_since_start.as_secs_f64() * 1000.0; let transit = arrival_ms - send_ms;
if let Some(prev_t) = self.prev_transit_ms {
let d = (transit - prev_t).abs();
self.interval_result.jitter_ms += (d - self.interval_result.jitter_ms) / 16.0;
}
self.prev_transit_ms = Some(transit);
}
pub(crate) fn calc_bitrate(&mut self, time: Duration) {
let received = self.interval_result.received;
let lost = self.interval_result.lost;
if received == 0 {
self.recommend_pps = 0.0;
return;
}
let act_pps = received as f64 / time.as_secs_f64();
if lost == 0 {
return;
}
let interval_secs = time.as_secs_f64();
if interval_secs <= f64::EPSILON {
return;
}
let received_ratio = ((received - lost) as f64 / received as f64) * 100.0;
let int_part = received_ratio as u32;
let decimal_part = ((received_ratio * 10000.0) as u32) % 100;
let recommended = if int_part < ACCEPTABLE {
act_pps * 0.95 } else if decimal_part >= ACCEPTABLEDECIMAL {
act_pps + 5.0 } else {
act_pps - 10.0 };
self.recommend_pps = recommended.max(0.0); }
pub(crate) fn get_interval_result(&mut self, iterval_time: Duration) -> IntervalResult {
self.interval_result.time = iterval_time;
let r = std::mem::take(&mut self.interval_result);
r
}
}
pub fn now_micros() -> (u64, u32) {
let d = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
(d.as_secs(), d.subsec_micros())
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_udp_header_new() {
let header = UdpHeader::new(12345, 1000000, 500000, FLAG_DATA);
assert_eq!(header.seq, 12345);
assert_eq!(header.sec, 1000000);
assert_eq!(header.usec, 500000);
assert_eq!(header.flags, FLAG_DATA);
}
#[test]
fn test_udp_header_write_and_read() {
let mut buffer = vec![0u8; HEADER_SIZE];
let mut original = UdpHeader::new(42, 1234567890, 999999, FLAG_FIN);
original.write_header(&mut buffer);
let read_header = UdpHeader::read_header(&mut buffer);
assert_eq!(read_header.seq, 42);
assert_eq!(read_header.sec, 1234567890);
assert_eq!(read_header.usec, 999999);
assert_eq!(read_header.flags, FLAG_FIN);
}
#[test]
#[should_panic]
fn test_udp_header_write_buffer_too_small() {
let mut buffer = vec![0u8; HEADER_SIZE - 1];
let mut header = UdpHeader::new(1, 2, 3, 4);
header.write_header(&mut buffer); }
#[test]
fn test_interval_result_default() {
let result = IntervalResult::default();
assert_eq!(result.received, 0);
assert_eq!(result.lost, 0);
assert_eq!(result.bytes, 0);
assert_eq!(result.jitter_ms, 0.0);
assert_eq!(result.out_of_order, 0);
assert_eq!(result.recommended_bitrate, 0);
assert_eq!(result.time, Duration::ZERO);
}
#[test]
fn test_udp_data_new() {
let data = UdpData::new();
assert_eq!(data.last_seq, None);
assert_eq!(data.interval_result.received, 0);
assert_eq!(data.prev_transit_ms, None);
assert_eq!(data.recommend_pps, 0.0);
}
#[test]
fn test_process_packet_jitter_calculation() {
let mut data = UdpData::new();
let h1 = UdpHeader::new(0, 1000, 0, FLAG_DATA);
data.process_packet(1500, &h1, Duration::from_millis(100));
assert!(data.prev_transit_ms.is_some());
assert_eq!(data.interval_result.jitter_ms, 0.0);
let h2 = UdpHeader::new(1, 1000, 50000, FLAG_DATA);
data.process_packet(1500, &h2, Duration::from_millis(200));
assert!(data.interval_result.jitter_ms > 0.0);
}
#[test]
fn test_process_multiple_packets() {
let mut data = UdpData::new();
for i in 0..10 {
if i == 3 {
continue; }
let seq = if i == 4 {
5
} else if i == 5 {
4
} else {
i
}; let header = UdpHeader::new(seq, 1000 + i, (i * 1000) as u32, FLAG_DATA);
data.process_packet(1500, &header, Duration::from_millis(i * 100));
}
assert_eq!(data.interval_result.received, 9); assert_eq!(data.interval_result.bytes, 13500); assert!(data.interval_result.lost > 0); assert_eq!(data.interval_result.out_of_order, 1); }
#[test]
fn test_large_sequence_numbers() {
let mut data = UdpData::new();
let large_seq = u64::MAX - 10;
let h1 = UdpHeader::new(large_seq, 1000, 0, FLAG_DATA);
data.process_packet(1500, &h1, Duration::from_secs(1));
let h2 = UdpHeader::new(large_seq + 1, 1000, 1000, FLAG_DATA);
data.process_packet(1500, &h2, Duration::from_secs(1));
assert_eq!(data.last_seq, Some(large_seq + 1));
assert_eq!(data.interval_result.lost, 0);
}
#[test]
fn test_calc_bitrate_high_loss() {
let mut data = UdpData::new();
data.interval_result.received = 1000;
data.interval_result.lost = 100;
data.calc_bitrate(Duration::from_secs(1));
assert_eq!(data.recommend_pps, 950.0);
}
#[test]
fn test_calc_bitrate_low_loss_acceptable() {
let mut data = UdpData::new();
data.interval_result.received = 1000;
data.interval_result.lost = 5;
data.calc_bitrate(Duration::from_secs(1));
assert_eq!(data.recommend_pps, 990.0);
}
#[test]
fn test_calc_bitrate_very_low_loss() {
let mut data = UdpData::new();
data.interval_result.received = 10000;
data.interval_result.lost = 1;
data.calc_bitrate(Duration::from_secs(1));
assert_eq!(data.recommend_pps, 9990.0);
}
#[test]
fn test_calc_bitrate_non_one_second_interval() {
let mut data = UdpData::new();
data.interval_result.received = 500;
data.interval_result.lost = 50;
data.calc_bitrate(Duration::from_millis(500));
assert_eq!(data.recommend_pps, 950.0);
}
#[test]
fn test_get_interval_result() {
let mut data = UdpData::new();
data.interval_result.received = 100;
data.interval_result.bytes = 150000;
data.interval_result.lost = 5;
data.interval_result.jitter_ms = 2.5;
data.interval_result.out_of_order = 3;
let result = data.get_interval_result(Duration::from_secs(1));
assert_eq!(result.received, 100);
assert_eq!(result.bytes, 150000);
assert_eq!(result.lost, 5);
assert_eq!(result.jitter_ms, 2.5);
assert_eq!(result.out_of_order, 3);
assert_eq!(data.interval_result.received, 0);
assert_eq!(data.interval_result.bytes, 0);
assert_eq!(data.interval_result.lost, 0);
assert_eq!(data.interval_result.jitter_ms, 0.0);
assert_eq!(data.interval_result.out_of_order, 0);
}
}