use std::time::SystemTime;
use crate::util::SystemTimeExt;
use super::{FeedbackMessageType, RtcpType, Ssrc};
use super::{RtcpHeader, RtcpPacket};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExtendedReport {
pub ssrc: Ssrc,
pub blocks: Vec<ReportBlock>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum ReportBlock {
Rrtr(Rrtr),
Dlrr(Dlrr),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
pub struct Rrtr {
pub ntp_time: SystemTime,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub struct Dlrr {
pub items: Vec<DlrrItem>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DlrrItem {
pub ssrc: Ssrc,
pub last_rr_time: u32,
pub last_rr_delay: u32,
}
impl RtcpPacket for ExtendedReport {
fn header(&self) -> RtcpHeader {
RtcpHeader {
rtcp_type: RtcpType::ExtendedReport,
feedback_message_type: FeedbackMessageType::NotUsed,
words_less_one: (self.length_words() - 1) as u16,
}
}
fn length_words(&self) -> usize {
let header = 1;
let ssrc = 1;
let blocks: usize = self.blocks.iter().map(|b| b.len() / 4).sum();
header + ssrc + blocks
}
fn write_to(&self, buf: &mut [u8]) -> usize {
let mut len = self.header().write_to(buf);
buf[4..8].copy_from_slice(&self.ssrc.to_be_bytes());
len += 4;
for block in self.blocks.iter() {
len += match block {
ReportBlock::Rrtr(b) => b.write_to(&mut buf[len..]),
ReportBlock::Dlrr(b) => b.write_to(&mut buf[len..]),
};
}
len
}
}
impl ReportBlock {
pub(crate) fn len(&self) -> usize {
match self {
Self::Rrtr(_) => Rrtr::len(),
Self::Dlrr(v) => v.len(),
}
}
}
impl Rrtr {
fn write_to(&self, buf: &mut [u8]) -> usize {
buf[0] = 4_u8;
buf[1] = 0_u8;
buf[2..4].copy_from_slice(&2_u16.to_be_bytes());
let mt = self.ntp_time.as_ntp_64();
buf[4..12].copy_from_slice(&mt.to_be_bytes());
12
}
fn len() -> usize {
12
}
}
impl Dlrr {
fn write_to(&self, buf: &mut [u8]) -> usize {
buf[0] = 5_u8;
buf[1] = 0_u8;
let len: u16 = self.items.len() as u16 * 12_u16;
buf[2..4].copy_from_slice(&len.to_be_bytes());
let mut buf = &mut buf[4..];
for item in self.items.iter() {
buf[0..4].copy_from_slice(&item.ssrc.to_be_bytes());
buf[4..8].copy_from_slice(&item.last_rr_time.to_be_bytes());
buf[8..12].copy_from_slice(&item.last_rr_delay.to_be_bytes());
buf = &mut buf[4..];
}
self.len()
}
fn len(&self) -> usize {
4 + (self.items.len() * 4 * 3)
}
}
impl<'a> TryFrom<&'a [u8]> for ExtendedReport {
type Error = &'static str;
fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
if buf.len() < 4 {
return Err("Less than 4 bytes for ExtendedReport");
}
let ssrc = u32::from_be_bytes(buf[..4].try_into().unwrap()).into();
let mut blocks: Vec<ReportBlock> = Vec::new();
let mut buf = &buf[4..];
while let Ok(block) = buf.try_into() {
let block: ReportBlock = block;
let len = block.len();
blocks.push(block);
buf = &buf[len..];
}
Ok(ExtendedReport { ssrc, blocks })
}
}
impl<'a> TryFrom<&'a [u8]> for ReportBlock {
type Error = &'static str;
fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
if buf.is_empty() {
return Err("not enough data");
}
let block_type: u8 = buf[0];
match block_type {
4 => {
let block = Rrtr::try_from(buf)?;
Ok(Self::Rrtr(block))
}
5 => {
let block = Dlrr::try_from(buf)?;
Ok(Self::Dlrr(block))
}
_ => Err("unknown block type"),
}
}
}
impl<'a> TryFrom<&'a [u8]> for Rrtr {
type Error = &'static str;
fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
let ntp_time = u64::from_be_bytes(buf[4..4 + 8].try_into().unwrap());
let ntp_time = SystemTime::from_ntp_64(ntp_time);
Ok(Rrtr { ntp_time })
}
}
impl<'a> TryFrom<&'a [u8]> for Dlrr {
type Error = &'static str;
fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
let words_per_block = 3;
let blocks = u16::from_be_bytes(buf[2..4].try_into().unwrap()) / words_per_block;
let mut items: Vec<DlrrItem> = Vec::with_capacity(blocks as usize);
let mut buf = &buf[4..];
for _ in 0..blocks {
let ssrc = u32::from_be_bytes(buf[0..4].try_into().unwrap()).into();
let last_rr_time = u32::from_be_bytes(buf[4..8].try_into().unwrap());
let last_rr_delay = u32::from_be_bytes(buf[8..12].try_into().unwrap());
items.push(DlrrItem {
ssrc,
last_rr_time,
last_rr_delay,
});
buf = &buf[12..];
}
Ok(Dlrr { items })
}
}