rvoip-rtp-core 0.2.4

RTP/RTCP protocol implementation for the rvoip stack
Documentation
use bytes::{Buf, BufMut, BytesMut};

use super::ntp::NtpTimestamp;
use super::report_block::RtcpReportBlock;
use crate::error::Error;
use crate::{Result, RtpSsrc};

/// RTCP Sender Report (SR) packet
/// Defined in RFC 3550 Section 6.4.1
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtcpSenderReport {
    /// SSRC of the sender
    pub ssrc: RtpSsrc,

    /// NTP timestamp of this report
    pub ntp_timestamp: NtpTimestamp,

    /// RTP timestamp corresponding to the NTP timestamp
    pub rtp_timestamp: u32,

    /// Sender's packet count
    pub sender_packet_count: u32,

    /// Sender's octet count
    pub sender_octet_count: u32,

    /// Report blocks
    pub report_blocks: Vec<RtcpReportBlock>,
}

impl RtcpSenderReport {
    /// Create a new sender report
    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(),
        }
    }

    /// Size of the sender info section in bytes
    pub const SENDER_INFO_SIZE: usize = 20;

    /// Add a report block
    pub fn add_report_block(&mut self, block: RtcpReportBlock) {
        self.report_blocks.push(block);
    }

    /// Calculate total size in bytes
    pub fn size(&self) -> usize {
        4 + // SSRC
        Self::SENDER_INFO_SIZE + // Sender info (NTP timestamp, RTP timestamp, packet count, octet count)
        self.report_blocks.len() * RtcpReportBlock::SIZE // Report blocks
    }

    /// Serialize the sender report to bytes
    pub fn serialize(&self) -> Result<BytesMut> {
        let mut buf = BytesMut::with_capacity(self.size());

        // SSRC
        buf.put_u32(self.ssrc);

        // NTP timestamp
        buf.put_u32(self.ntp_timestamp.seconds);
        buf.put_u32(self.ntp_timestamp.fraction);

        // RTP timestamp
        buf.put_u32(self.rtp_timestamp);

        // Packet count
        buf.put_u32(self.sender_packet_count);

        // Octet count
        buf.put_u32(self.sender_octet_count);

        // Report blocks
        for block in &self.report_blocks {
            block.serialize(&mut buf)?;
        }

        Ok(buf)
    }
}

/// Parse a sender report from RTCP packet data
pub fn parse_sender_report(buf: &mut impl Buf, report_count: u8) -> Result<RtcpSenderReport> {
    // Check if we have enough data for the sender info and SSRC (24 bytes total)
    if buf.remaining() < 24 {
        return Err(Error::BufferTooSmall {
            required: 24,
            available: buf.remaining(),
        });
    }

    let ssrc = buf.get_u32();

    // NTP timestamp (64 bits)
    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();

    // Parse report blocks
    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,
    })
}

/// Serialize a sender report
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);

        // Base size (SSRC + sender info)
        assert_eq!(sr.size(), 4 + RtcpSenderReport::SENDER_INFO_SIZE);

        // Add a report block
        sr.add_report_block(RtcpReportBlock::new(0xabcdef01));

        // Size should increase by the report block size
        assert_eq!(
            sr.size(),
            4 + RtcpSenderReport::SENDER_INFO_SIZE + RtcpReportBlock::SIZE
        );
    }

    #[test]
    fn test_serialize_parse() {
        // Create a sender report with one report block
        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);

        // Serialize
        let serialized = original.serialize().unwrap();

        // Parse
        let parsed = parse_sender_report(&mut serialized.freeze(), 1).unwrap();

        // Verify
        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
        );
    }
}