use bytes::{Buf, BufMut, BytesMut};
use super::report_block::RtcpReportBlock;
use crate::error::Error;
use crate::{Result, RtpSsrc};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtcpReceiverReport {
pub ssrc: RtpSsrc,
pub report_blocks: Vec<RtcpReportBlock>,
}
impl RtcpReceiverReport {
pub fn new(ssrc: RtpSsrc) -> Self {
Self {
ssrc,
report_blocks: Vec::new(),
}
}
pub fn add_report_block(&mut self, block: RtcpReportBlock) {
self.report_blocks.push(block);
}
pub fn size(&self) -> usize {
4 + 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);
for block in &self.report_blocks {
block.serialize(&mut buf)?;
}
Ok(buf)
}
}
pub fn parse_receiver_report(buf: &mut impl Buf, report_count: u8) -> Result<RtcpReceiverReport> {
if buf.remaining() < 4 {
return Err(Error::BufferTooSmall {
required: 4,
available: buf.remaining(),
});
}
let ssrc = 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(RtcpReceiverReport {
ssrc,
report_blocks,
})
}
pub fn serialize_receiver_report(rr: &RtcpReceiverReport) -> Result<BytesMut> {
rr.serialize()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_receiver_report_creation() {
let rr = RtcpReceiverReport::new(0x12345678);
assert_eq!(rr.ssrc, 0x12345678);
assert!(rr.report_blocks.is_empty());
}
#[test]
fn test_add_report_block() {
let mut rr = RtcpReceiverReport::new(0x12345678);
let block1 = RtcpReportBlock::new(0xabcdef01);
let block2 = RtcpReportBlock::new(0x11223344);
rr.add_report_block(block1);
rr.add_report_block(block2);
assert_eq!(rr.report_blocks.len(), 2);
assert_eq!(rr.report_blocks[0].ssrc, 0xabcdef01);
assert_eq!(rr.report_blocks[1].ssrc, 0x11223344);
}
#[test]
fn test_size_calculation() {
let mut rr = RtcpReceiverReport::new(0x12345678);
assert_eq!(rr.size(), 4);
rr.add_report_block(RtcpReportBlock::new(0xabcdef01));
rr.add_report_block(RtcpReportBlock::new(0x11223344));
assert_eq!(rr.size(), 4 + 2 * RtcpReportBlock::SIZE);
}
#[test]
fn test_serialize_parse() {
let mut original = RtcpReceiverReport::new(0x12345678);
let block1 = RtcpReportBlock {
ssrc: 0xabcdef01,
fraction_lost: 42,
cumulative_lost: 1000,
highest_seq: 5000,
jitter: 100,
last_sr: 0x87654321,
delay_since_last_sr: 1500,
};
let block2 = RtcpReportBlock {
ssrc: 0x11223344,
fraction_lost: 10,
cumulative_lost: 500,
highest_seq: 10000,
jitter: 200,
last_sr: 0x55667788,
delay_since_last_sr: 2000,
};
original.add_report_block(block1);
original.add_report_block(block2);
let serialized = original.serialize().unwrap();
let parsed = parse_receiver_report(&mut serialized.freeze(), 2).unwrap();
assert_eq!(parsed.ssrc, original.ssrc);
assert_eq!(parsed.report_blocks.len(), original.report_blocks.len());
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
);
assert_eq!(parsed.report_blocks[1].ssrc, original.report_blocks[1].ssrc);
assert_eq!(
parsed.report_blocks[1].fraction_lost,
original.report_blocks[1].fraction_lost
);
assert_eq!(
parsed.report_blocks[1].cumulative_lost,
original.report_blocks[1].cumulative_lost
);
assert_eq!(
parsed.report_blocks[1].highest_seq,
original.report_blocks[1].highest_seq
);
assert_eq!(
parsed.report_blocks[1].jitter,
original.report_blocks[1].jitter
);
assert_eq!(
parsed.report_blocks[1].last_sr,
original.report_blocks[1].last_sr
);
assert_eq!(
parsed.report_blocks[1].delay_since_last_sr,
original.report_blocks[1].delay_since_last_sr
);
}
}