use bytes::{Buf, BufMut, BytesMut};
use super::ntp::NtpTimestamp;
use super::report_block::RtcpReportBlock;
use crate::error::Error;
use crate::{Result, RtpSsrc};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtcpSenderReport {
pub ssrc: RtpSsrc,
pub ntp_timestamp: NtpTimestamp,
pub rtp_timestamp: u32,
pub sender_packet_count: u32,
pub sender_octet_count: u32,
pub report_blocks: Vec<RtcpReportBlock>,
}
impl RtcpSenderReport {
pub fn new(ssrc: RtpSsrc) -> Self {
Self {
ssrc,
ntp_timestamp: NtpTimestamp::now(),
rtp_timestamp: 0,
sender_packet_count: 0,
sender_octet_count: 0,
report_blocks: Vec::new(),
}
}
pub const SENDER_INFO_SIZE: usize = 20;
pub fn add_report_block(&mut self, block: RtcpReportBlock) {
self.report_blocks.push(block);
}
pub fn size(&self) -> usize {
4 + Self::SENDER_INFO_SIZE + self.report_blocks.len() * RtcpReportBlock::SIZE }
pub fn serialize(&self) -> Result<BytesMut> {
let mut buf = BytesMut::with_capacity(self.size());
buf.put_u32(self.ssrc);
buf.put_u32(self.ntp_timestamp.seconds);
buf.put_u32(self.ntp_timestamp.fraction);
buf.put_u32(self.rtp_timestamp);
buf.put_u32(self.sender_packet_count);
buf.put_u32(self.sender_octet_count);
for block in &self.report_blocks {
block.serialize(&mut buf)?;
}
Ok(buf)
}
}
pub fn parse_sender_report(buf: &mut impl Buf, report_count: u8) -> Result<RtcpSenderReport> {
if buf.remaining() < 24 {
return Err(Error::BufferTooSmall {
required: 24,
available: buf.remaining(),
});
}
let ssrc = buf.get_u32();
let ntp_seconds = buf.get_u32();
let ntp_fraction = buf.get_u32();
let ntp_timestamp = NtpTimestamp {
seconds: ntp_seconds,
fraction: ntp_fraction,
};
let rtp_timestamp = buf.get_u32();
let sender_packet_count = buf.get_u32();
let sender_octet_count = buf.get_u32();
let mut report_blocks = Vec::with_capacity(report_count as usize);
for _ in 0..report_count {
report_blocks.push(RtcpReportBlock::parse(buf)?);
}
Ok(RtcpSenderReport {
ssrc,
ntp_timestamp,
rtp_timestamp,
sender_packet_count,
sender_octet_count,
report_blocks,
})
}
pub fn serialize_sender_report(sr: &RtcpSenderReport) -> Result<BytesMut> {
sr.serialize()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sender_report_creation() {
let sr = RtcpSenderReport::new(0x12345678);
assert_eq!(sr.ssrc, 0x12345678);
assert!(sr.report_blocks.is_empty());
}
#[test]
fn test_add_report_block() {
let mut sr = RtcpSenderReport::new(0x12345678);
let block = RtcpReportBlock::new(0xabcdef01);
sr.add_report_block(block);
assert_eq!(sr.report_blocks.len(), 1);
assert_eq!(sr.report_blocks[0].ssrc, 0xabcdef01);
}
#[test]
fn test_size_calculation() {
let mut sr = RtcpSenderReport::new(0x12345678);
assert_eq!(sr.size(), 4 + RtcpSenderReport::SENDER_INFO_SIZE);
sr.add_report_block(RtcpReportBlock::new(0xabcdef01));
assert_eq!(
sr.size(),
4 + RtcpSenderReport::SENDER_INFO_SIZE + RtcpReportBlock::SIZE
);
}
#[test]
fn test_serialize_parse() {
let mut original = RtcpSenderReport::new(0x12345678);
original.ntp_timestamp = NtpTimestamp {
seconds: 0x11223344,
fraction: 0x55667788,
};
original.rtp_timestamp = 0x99aabbcc;
original.sender_packet_count = 1000;
original.sender_octet_count = 100000;
let block = RtcpReportBlock {
ssrc: 0xabcdef01,
fraction_lost: 42,
cumulative_lost: 1000,
highest_seq: 5000,
jitter: 100,
last_sr: 0x87654321,
delay_since_last_sr: 1500,
};
original.add_report_block(block);
let serialized = original.serialize().unwrap();
let parsed = parse_sender_report(&mut serialized.freeze(), 1).unwrap();
assert_eq!(parsed.ssrc, original.ssrc);
assert_eq!(parsed.ntp_timestamp.seconds, original.ntp_timestamp.seconds);
assert_eq!(
parsed.ntp_timestamp.fraction,
original.ntp_timestamp.fraction
);
assert_eq!(parsed.rtp_timestamp, original.rtp_timestamp);
assert_eq!(parsed.sender_packet_count, original.sender_packet_count);
assert_eq!(parsed.sender_octet_count, original.sender_octet_count);
assert_eq!(parsed.report_blocks.len(), 1);
assert_eq!(parsed.report_blocks[0].ssrc, original.report_blocks[0].ssrc);
assert_eq!(
parsed.report_blocks[0].fraction_lost,
original.report_blocks[0].fraction_lost
);
assert_eq!(
parsed.report_blocks[0].cumulative_lost,
original.report_blocks[0].cumulative_lost
);
assert_eq!(
parsed.report_blocks[0].highest_seq,
original.report_blocks[0].highest_seq
);
assert_eq!(
parsed.report_blocks[0].jitter,
original.report_blocks[0].jitter
);
assert_eq!(
parsed.report_blocks[0].last_sr,
original.report_blocks[0].last_sr
);
assert_eq!(
parsed.report_blocks[0].delay_since_last_sr,
original.report_blocks[0].delay_since_last_sr
);
}
}