use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Instant, SystemTime, UNIX_EPOCH};
use super::clock::MediaClock;
use crate::packet::rtcp::NtpTimestamp;
use crate::RtpSsrc;
use crate::RtpTimestamp;
#[allow(dead_code)] struct StreamMapping {
#[allow(dead_code)] source_ssrc: RtpSsrc,
#[allow(dead_code)] target_ssrc: RtpSsrc,
source_rate: u32,
target_rate: u32,
source_rtp_ref: RtpTimestamp,
target_rtp_ref: RtpTimestamp,
ntp_ref: NtpTimestamp,
last_update: Instant,
drift_ppm: f64,
}
impl StreamMapping {
pub fn new(
source_ssrc: RtpSsrc,
target_ssrc: RtpSsrc,
source_rate: u32,
target_rate: u32,
source_rtp: RtpTimestamp,
target_rtp: RtpTimestamp,
ntp: NtpTimestamp,
) -> Self {
Self {
source_ssrc,
target_ssrc,
source_rate,
target_rate,
source_rtp_ref: source_rtp,
target_rtp_ref: target_rtp,
ntp_ref: ntp,
last_update: Instant::now(),
drift_ppm: 0.0,
}
}
pub fn map_timestamp(&self, source_rtp: RtpTimestamp) -> RtpTimestamp {
let source_diff = source_rtp.wrapping_sub(self.source_rtp_ref) as i64;
let seconds = source_diff as f64 / self.source_rate as f64;
let corrected_seconds = seconds * (1.0 + self.drift_ppm / 1_000_000.0);
let target_diff = (corrected_seconds * self.target_rate as f64) as i64;
self.target_rtp_ref.wrapping_add(target_diff as u32)
}
pub fn update(
&mut self,
source_rtp: RtpTimestamp,
target_rtp: RtpTimestamp,
ntp: NtpTimestamp,
) -> f64 {
let now = Instant::now();
let elapsed = now.duration_since(self.last_update).as_secs_f64();
if elapsed > 0.0 {
let source_ticks = source_rtp.wrapping_sub(self.source_rtp_ref) as i64;
let target_ticks = target_rtp.wrapping_sub(self.target_rtp_ref) as i64;
let source_seconds = source_ticks as f64 / self.source_rate as f64;
let target_seconds = target_ticks as f64 / self.target_rate as f64;
let drift = (target_seconds - source_seconds) / elapsed;
self.drift_ppm = drift * 1_000_000.0;
}
self.source_rtp_ref = source_rtp;
self.target_rtp_ref = target_rtp;
self.ntp_ref = ntp;
self.last_update = now;
self.drift_ppm / 1000.0
}
}
#[derive(Clone)]
pub struct TimestampMapper {
mappings: Arc<Mutex<HashMap<(RtpSsrc, RtpSsrc), StreamMapping>>>,
clocks: Arc<Mutex<HashMap<RtpSsrc, MediaClock>>>,
}
impl TimestampMapper {
pub fn new() -> Self {
Self {
mappings: Arc::new(Mutex::new(HashMap::new())),
clocks: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn register_stream(&self, ssrc: RtpSsrc, clock_rate: u32, initial_rtp: RtpTimestamp) {
if let Ok(mut clocks) = self.clocks.lock() {
let clock = MediaClock::now(clock_rate, initial_rtp);
clocks.insert(ssrc, clock);
}
}
pub fn update_from_sr(&self, ssrc: RtpSsrc, ntp: NtpTimestamp, rtp: RtpTimestamp) {
if let Ok(mut clocks) = self.clocks.lock() {
if let Some(clock) = clocks.get_mut(&ssrc) {
clock.update_reference(rtp, ntp);
} else {
let clock_rate = 8000; let clock = MediaClock::new(clock_rate, rtp, ntp);
clocks.insert(ssrc, clock);
}
}
}
pub fn map_streams(
&self,
source_ssrc: RtpSsrc,
target_ssrc: RtpSsrc,
source_rtp: RtpTimestamp,
target_rtp: RtpTimestamp,
ntp: NtpTimestamp,
) -> Option<f64> {
let (source_rate, target_rate) = if let Ok(clocks) = self.clocks.lock() {
let source_clock = clocks.get(&source_ssrc)?;
let target_clock = clocks.get(&target_ssrc)?;
(source_clock.clock_rate(), target_clock.clock_rate())
} else {
return None;
};
if let Ok(mut mappings) = self.mappings.lock() {
let key = (source_ssrc, target_ssrc);
if let Some(mapping) = mappings.get_mut(&key) {
let drift = mapping.update(source_rtp, target_rtp, ntp);
Some(drift)
} else {
let mapping = StreamMapping::new(
source_ssrc,
target_ssrc,
source_rate,
target_rate,
source_rtp,
target_rtp,
ntp,
);
mappings.insert(key, mapping);
Some(0.0) }
} else {
None
}
}
pub fn map_timestamp(
&self,
source_ssrc: RtpSsrc,
target_ssrc: RtpSsrc,
source_rtp: RtpTimestamp,
) -> Option<RtpTimestamp> {
if let Ok(mappings) = self.mappings.lock() {
let key = (source_ssrc, target_ssrc);
if let Some(mapping) = mappings.get(&key) {
Some(mapping.map_timestamp(source_rtp))
} else {
if let Ok(clocks) = self.clocks.lock() {
let source_clock = clocks.get(&source_ssrc)?;
let target_clock = clocks.get(&target_ssrc)?;
let ntp = source_clock.rtp_to_ntp(source_rtp);
Some(target_clock.ntp_to_rtp(ntp))
} else {
None
}
}
} else {
None
}
}
pub fn get_drift(&self, source_ssrc: RtpSsrc, target_ssrc: RtpSsrc) -> Option<f64> {
if let Ok(mappings) = self.mappings.lock() {
let key = (source_ssrc, target_ssrc);
mappings.get(&key).map(|mapping| mapping.drift_ppm)
} else {
None
}
}
pub fn rtp_to_wallclock(&self, ssrc: RtpSsrc, rtp: RtpTimestamp) -> Option<Instant> {
if let Ok(clocks) = self.clocks.lock() {
let clock = clocks.get(&ssrc)?;
Some(clock.rtp_to_system_time(rtp))
} else {
None
}
}
pub fn wallclock_to_rtp(&self, ssrc: RtpSsrc, time: Instant) -> Option<RtpTimestamp> {
if let Ok(clocks) = self.clocks.lock() {
let clock = clocks.get(&ssrc)?;
Some(clock.system_time_to_rtp(time))
} else {
None
}
}
pub fn get_sync_offset(&self, source_ssrc: RtpSsrc, target_ssrc: RtpSsrc) -> Option<f64> {
if let Ok(mappings) = self.mappings.lock() {
let key = (source_ssrc, target_ssrc);
if let Some(mapping) = mappings.get(&key) {
let elapsed = Instant::now()
.duration_since(mapping.last_update)
.as_secs_f64();
let drift_ms = (mapping.drift_ppm / 1000.0) * elapsed;
return Some(drift_ms);
}
}
if let Ok(clocks) = self.clocks.lock() {
let source_clock = clocks.get(&source_ssrc)?;
let target_clock = clocks.get(&target_ssrc)?;
let now = SystemTime::now().duration_since(UNIX_EPOCH).ok()?;
let ntp = NtpTimestamp::from_duration_since_unix_epoch(now);
let source_rtp = source_clock.ntp_to_rtp(ntp);
let target_rtp = target_clock.ntp_to_rtp(ntp);
let source_seconds = source_rtp as f64 / source_clock.clock_rate() as f64;
let target_seconds = target_rtp as f64 / target_clock.clock_rate() as f64;
let offset_ms = (target_seconds - source_seconds) * 1000.0;
Some(offset_ms)
} else {
None
}
}
}
impl Default for TimestampMapper {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stream_mapping() {
let source_ssrc = 0x1234;
let target_ssrc = 0x5678;
let source_rate = 8000; let target_rate = 90000; let source_rtp = 1600; let target_rtp = 18000; let ntp = NtpTimestamp::now();
let mapping = StreamMapping::new(
source_ssrc,
target_ssrc,
source_rate,
target_rate,
source_rtp,
target_rtp,
ntp,
);
let source_rtp_400ms = source_rtp.wrapping_add(3200);
let target_rtp_400ms = mapping.map_timestamp(source_rtp_400ms);
let expected = target_rtp + 36000;
assert_eq!(target_rtp_400ms, expected);
}
#[test]
fn test_timestamp_mapper() {
let mapper = TimestampMapper::new();
let audio_ssrc = 0x1234;
let video_ssrc = 0x5678;
let audio_rate = 8000; let video_rate = 90000;
mapper.register_stream(audio_ssrc, audio_rate, 800); mapper.register_stream(video_ssrc, video_rate, 9000);
let ntp = NtpTimestamp::now();
let audio_rtp_200ms = 1600; let video_rtp_200ms = 18000;
mapper.map_streams(
audio_ssrc,
video_ssrc,
audio_rtp_200ms,
video_rtp_200ms,
ntp,
);
let audio_rtp_400ms = 3200; let video_rtp_400ms = mapper.map_timestamp(audio_ssrc, video_ssrc, audio_rtp_400ms);
assert_eq!(video_rtp_400ms, Some(video_rtp_200ms + 18000));
}
}