#[cfg(test)]
mod cc_feedback_report_test;
use crate::{header::*, packet::*, util::*};
use shared::{
error::{Error, Result},
marshal::{Marshal, MarshalSize, Unmarshal},
};
use bytes::{Buf, BufMut};
use std::any::Any;
use std::fmt;
const REPORT_BLOCK_OFFSET: usize = HEADER_LENGTH + SSRC_LENGTH;
const REPORT_TIMESTAMP_LENGTH: usize = 4;
const REPORT_BLOCK_HEADER_LENGTH: usize = 8;
const METRIC_BLOCK_LENGTH: usize = 2;
const MAX_METRIC_BLOCKS: usize = 16384;
#[derive(Debug, PartialEq, Eq, Default, Clone, Copy)]
#[repr(u8)]
pub enum Ecn {
#[default]
NotEct = 0,
Ect1 = 1,
Ect0 = 2,
Ce = 3,
}
impl Ecn {
fn from_bits(value: u8) -> Self {
match value & 0x03 {
0 => Ecn::NotEct,
1 => Ecn::Ect1,
2 => Ecn::Ect0,
_ => Ecn::Ce,
}
}
}
impl fmt::Display for Ecn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Ecn::NotEct => "Not-ECT (00)",
Ecn::Ect1 => "ECT(1) (01)",
Ecn::Ect0 => "ECT(0) (10)",
Ecn::Ce => "CE (11)",
};
write!(f, "{s}")
}
}
#[derive(Debug, PartialEq, Eq, Default, Clone, Copy)]
pub struct CcFeedbackMetricBlock {
pub received: bool,
pub ecn: Ecn,
pub arrival_time_offset: u16,
}
impl CcFeedbackMetricBlock {
fn marshal_word(&self) -> Result<u16> {
let received = u16::from(self.received);
let word = set_nbits_of_uint16(0, 1, 0, received)?;
let word = set_nbits_of_uint16(word, 2, 1, self.ecn as u16)?;
set_nbits_of_uint16(word, 13, 3, self.arrival_time_offset)
}
fn unmarshal_word(word: u16) -> Self {
let received = word & 0x8000 != 0;
if !received {
return Self::default();
}
Self {
received,
ecn: Ecn::from_bits((word >> 13) as u8),
arrival_time_offset: word & 0x1FFF,
}
}
}
impl fmt::Display for CcFeedbackMetricBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.received {
write!(f, "(rx, {}, {}/1024s)", self.ecn, self.arrival_time_offset)
} else {
write!(f, "(lost)")
}
}
}
#[derive(Debug, PartialEq, Eq, Default, Clone)]
pub struct CcFeedbackReportBlock {
pub media_ssrc: u32,
pub begin_sequence: u16,
pub metric_blocks: Vec<CcFeedbackMetricBlock>,
}
impl CcFeedbackReportBlock {
fn raw_size(&self) -> usize {
REPORT_BLOCK_HEADER_LENGTH + METRIC_BLOCK_LENGTH * self.padded_metric_block_count()
}
fn padded_metric_block_count(&self) -> usize {
let count = self.metric_blocks.len();
if count.is_multiple_of(2) {
count
} else {
count + 1
}
}
fn marshal_to(&self, buf: &mut &mut [u8]) -> Result<()> {
if self.metric_blocks.len() > MAX_METRIC_BLOCKS {
return Err(Error::TooManyReports);
}
buf.put_u32(self.media_ssrc);
buf.put_u16(self.begin_sequence);
buf.put_u16(self.metric_blocks.len() as u16);
for block in &self.metric_blocks {
buf.put_u16(block.marshal_word()?);
}
for _ in self.metric_blocks.len()..self.padded_metric_block_count() {
buf.put_u16(0);
}
Ok(())
}
fn unmarshal_from<B: Buf>(raw_packet: &mut B, budget: usize) -> Result<(Self, usize)> {
if budget < REPORT_BLOCK_HEADER_LENGTH
|| raw_packet.remaining() < REPORT_BLOCK_HEADER_LENGTH
{
return Err(Error::PacketTooShort);
}
let media_ssrc = raw_packet.get_u32();
let begin_sequence = raw_packet.get_u16();
let num_reports = raw_packet.get_u16() as usize;
let padded = if num_reports.is_multiple_of(2) {
num_reports
} else {
num_reports + 1
};
let consumed = REPORT_BLOCK_HEADER_LENGTH + METRIC_BLOCK_LENGTH * padded;
if budget < consumed || raw_packet.remaining() < METRIC_BLOCK_LENGTH * padded {
return Err(Error::PacketTooShort);
}
let mut metric_blocks = Vec::with_capacity(num_reports);
for _ in 0..num_reports {
metric_blocks.push(CcFeedbackMetricBlock::unmarshal_word(raw_packet.get_u16()));
}
if padded != num_reports {
raw_packet.advance(METRIC_BLOCK_LENGTH);
}
Ok((
Self {
media_ssrc,
begin_sequence,
metric_blocks,
},
consumed,
))
}
}
impl fmt::Display for CcFeedbackReportBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"\tssrc={:x} begin_seq={} reports={}\n\t\t",
self.media_ssrc,
self.begin_sequence,
self.metric_blocks.len()
)?;
for block in &self.metric_blocks {
write!(f, "{block} ")?;
}
writeln!(f)
}
}
#[derive(Debug, PartialEq, Eq, Default, Clone)]
pub struct CcFeedbackReport {
pub sender_ssrc: u32,
pub report_blocks: Vec<CcFeedbackReportBlock>,
pub report_timestamp: u32,
}
impl fmt::Display for CcFeedbackReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"CcFeedbackReport sender_ssrc={:x} report_timestamp={}",
self.sender_ssrc, self.report_timestamp
)?;
for block in &self.report_blocks {
write!(f, "{block}")?;
}
Ok(())
}
}
impl Packet for CcFeedbackReport {
fn header(&self) -> Header {
Header {
padding: get_padding_size(self.raw_size()) != 0,
count: FORMAT_CCFB,
packet_type: PacketType::TransportSpecificFeedback,
length: ((self.marshal_size() / 4) - 1) as u16,
}
}
fn destination_ssrc(&self) -> Vec<u32> {
self.report_blocks
.iter()
.map(|block| block.media_ssrc)
.collect()
}
fn raw_size(&self) -> usize {
let blocks: usize = self.report_blocks.iter().map(|b| b.raw_size()).sum();
REPORT_BLOCK_OFFSET + blocks + REPORT_TIMESTAMP_LENGTH
}
fn as_any(&self) -> &dyn Any {
self
}
fn equal(&self, other: &dyn Packet) -> bool {
other.as_any().downcast_ref::<CcFeedbackReport>() == Some(self)
}
fn cloned(&self) -> Box<dyn Packet> {
Box::new(self.clone())
}
}
impl MarshalSize for CcFeedbackReport {
fn marshal_size(&self) -> usize {
let l = self.raw_size();
l + get_padding_size(l)
}
}
impl Marshal for CcFeedbackReport {
fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
if buf.remaining_mut() < self.marshal_size() {
return Err(Error::BufferTooShort);
}
let h = self.header();
let n = h.marshal_to(buf)?;
buf = &mut buf[n..];
buf.put_u32(self.sender_ssrc);
for block in &self.report_blocks {
block.marshal_to(&mut buf)?;
}
buf.put_u32(self.report_timestamp);
if h.padding {
put_padding(buf, self.raw_size());
}
Ok(self.marshal_size())
}
}
impl Unmarshal for CcFeedbackReport {
fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
where
Self: Sized,
B: Buf,
{
let raw_packet_len = raw_packet.remaining();
if raw_packet_len < REPORT_BLOCK_OFFSET + REPORT_TIMESTAMP_LENGTH {
return Err(Error::PacketTooShort);
}
let h = Header::unmarshal(raw_packet)?;
if h.packet_type != PacketType::TransportSpecificFeedback || h.count != FORMAT_CCFB {
return Err(Error::WrongType);
}
let packet_len = 4 * (h.length as usize + 1);
if raw_packet_len < packet_len || packet_len < REPORT_BLOCK_OFFSET + REPORT_TIMESTAMP_LENGTH
{
return Err(Error::PacketTooShort);
}
let sender_ssrc = raw_packet.get_u32();
let mut remaining_blocks_len = packet_len - REPORT_BLOCK_OFFSET - REPORT_TIMESTAMP_LENGTH;
let mut report_blocks = Vec::new();
while remaining_blocks_len > 0 {
let (block, consumed) =
CcFeedbackReportBlock::unmarshal_from(raw_packet, remaining_blocks_len)?;
report_blocks.push(block);
remaining_blocks_len -= consumed;
}
let report_timestamp = raw_packet.get_u32();
Ok(CcFeedbackReport {
sender_ssrc,
report_blocks,
report_timestamp,
})
}
}