use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::error::Error;
use crate::{Result, RtpSsrc};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RtcpPacketType {
SenderReport = 200,
ReceiverReport = 201,
SourceDescription = 202,
Goodbye = 203,
ApplicationDefined = 204,
}
impl TryFrom<u8> for RtcpPacketType {
type Error = Error;
fn try_from(value: u8) -> Result<Self> {
match value {
200 => Ok(RtcpPacketType::SenderReport),
201 => Ok(RtcpPacketType::ReceiverReport),
202 => Ok(RtcpPacketType::SourceDescription),
203 => Ok(RtcpPacketType::Goodbye),
204 => Ok(RtcpPacketType::ApplicationDefined),
_ => Err(Error::RtcpError(format!(
"Unknown RTCP packet type: {}",
value
))),
}
}
}
pub const RTCP_VERSION: u8 = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct NtpTimestamp {
pub seconds: u32,
pub fraction: u32,
}
impl NtpTimestamp {
pub fn now() -> Self {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_else(|_| Duration::from_secs(0));
let ntp_seconds = now.as_secs() + 2208988800;
let nanos = now.subsec_nanos();
let ntp_fraction = (nanos as u64 * 0x100000000u64 / 1_000_000_000) as u32;
Self {
seconds: ntp_seconds as u32,
fraction: ntp_fraction,
}
}
pub fn to_u64(&self) -> u64 {
(self.seconds as u64) << 32 | (self.fraction as u64)
}
pub fn from_u64(value: u64) -> Self {
Self {
seconds: (value >> 32) as u32,
fraction: value as u32,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtcpReportBlock {
pub ssrc: RtpSsrc,
pub fraction_lost: u8,
pub cumulative_lost: u32,
pub highest_seq: u32,
pub jitter: u32,
pub last_sr: u32,
pub delay_since_last_sr: u32,
}
impl RtcpReportBlock {
pub fn new(ssrc: RtpSsrc) -> Self {
Self {
ssrc,
fraction_lost: 0,
cumulative_lost: 0,
highest_seq: 0,
jitter: 0,
last_sr: 0,
delay_since_last_sr: 0,
}
}
pub const SIZE: usize = 24;
pub fn parse(buf: &mut impl Buf) -> Result<Self> {
if buf.remaining() < Self::SIZE {
return Err(Error::BufferTooSmall {
required: Self::SIZE,
available: buf.remaining(),
});
}
let ssrc = buf.get_u32();
let fraction_lost = buf.get_u8();
let cumulative_lost =
(buf.get_u8() as u32) << 16 | (buf.get_u8() as u32) << 8 | buf.get_u8() as u32;
let highest_seq = buf.get_u32();
let jitter = buf.get_u32();
let last_sr = buf.get_u32();
let delay_since_last_sr = buf.get_u32();
Ok(Self {
ssrc,
fraction_lost,
cumulative_lost,
highest_seq,
jitter,
last_sr,
delay_since_last_sr,
})
}
pub fn serialize(&self, buf: &mut BytesMut) -> Result<()> {
if buf.remaining_mut() < Self::SIZE {
buf.reserve(Self::SIZE - buf.remaining_mut());
}
buf.put_u32(self.ssrc);
buf.put_u8(self.fraction_lost);
buf.put_u8(((self.cumulative_lost >> 16) & 0xFF) as u8);
buf.put_u8(((self.cumulative_lost >> 8) & 0xFF) as u8);
buf.put_u8((self.cumulative_lost & 0xFF) as u8);
buf.put_u32(self.highest_seq);
buf.put_u32(self.jitter);
buf.put_u32(self.last_sr);
buf.put_u32(self.delay_since_last_sr);
Ok(())
}
}
#[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;
}
#[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(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RtcpSdesItemType {
End = 0,
CName = 1,
Name = 2,
Email = 3,
Phone = 4,
Location = 5,
Tool = 6,
Note = 7,
Private = 8,
}
impl TryFrom<u8> for RtcpSdesItemType {
type Error = Error;
fn try_from(value: u8) -> Result<Self> {
match value {
0 => Ok(RtcpSdesItemType::End),
1 => Ok(RtcpSdesItemType::CName),
2 => Ok(RtcpSdesItemType::Name),
3 => Ok(RtcpSdesItemType::Email),
4 => Ok(RtcpSdesItemType::Phone),
5 => Ok(RtcpSdesItemType::Location),
6 => Ok(RtcpSdesItemType::Tool),
7 => Ok(RtcpSdesItemType::Note),
8 => Ok(RtcpSdesItemType::Private),
_ => Err(Error::RtcpError(format!(
"Unknown SDES item type: {}",
value
))),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtcpSdesItem {
pub item_type: RtcpSdesItemType,
pub value: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtcpSdesChunk {
pub ssrc: RtpSsrc,
pub items: Vec<RtcpSdesItem>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtcpSourceDescription {
pub chunks: Vec<RtcpSdesChunk>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtcpGoodbye {
pub sources: Vec<RtpSsrc>,
pub reason: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RtcpApplicationDefined {
pub ssrc: RtpSsrc,
pub name: [u8; 4],
pub data: Bytes,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RtcpPacket {
SenderReport(RtcpSenderReport),
ReceiverReport(RtcpReceiverReport),
SourceDescription(RtcpSourceDescription),
Goodbye(RtcpGoodbye),
ApplicationDefined(RtcpApplicationDefined),
}
impl RtcpPacket {
pub fn parse(data: &[u8]) -> Result<Self> {
let mut buf = Bytes::copy_from_slice(data);
if buf.remaining() < 4 {
return Err(Error::BufferTooSmall {
required: 4,
available: buf.remaining(),
});
}
let first_byte = buf.get_u8();
let version = (first_byte >> 6) & 0x03;
if version != RTCP_VERSION {
return Err(Error::RtcpError(format!(
"Invalid RTCP version: {}",
version
)));
}
let _padding = ((first_byte >> 5) & 0x01) != 0;
let report_count = first_byte & 0x1F;
let packet_type = RtcpPacketType::try_from(buf.get_u8())?;
let length = buf.get_u16() as usize * 4;
if buf.remaining() < length {
return Err(Error::BufferTooSmall {
required: length,
available: buf.remaining(),
});
}
match packet_type {
RtcpPacketType::SenderReport => {
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(&mut buf)?);
}
Ok(RtcpPacket::SenderReport(RtcpSenderReport {
ssrc,
ntp_timestamp,
rtp_timestamp,
sender_packet_count,
sender_octet_count,
report_blocks,
}))
}
RtcpPacketType::ReceiverReport => {
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(&mut buf)?);
}
Ok(RtcpPacket::ReceiverReport(RtcpReceiverReport {
ssrc,
report_blocks,
}))
}
RtcpPacketType::SourceDescription => {
Ok(RtcpPacket::SourceDescription(RtcpSourceDescription {
chunks: Vec::new(),
}))
}
RtcpPacketType::Goodbye => Ok(RtcpPacket::Goodbye(RtcpGoodbye {
sources: Vec::new(),
reason: None,
})),
RtcpPacketType::ApplicationDefined => {
Ok(RtcpPacket::ApplicationDefined(RtcpApplicationDefined {
ssrc: 0,
name: [0; 4],
data: Bytes::new(),
}))
}
}
}
}