use std::convert::TryInto;
use bytes::Bytes;
use crate::PacketContext;
#[derive(Eq, PartialEq)]
pub struct ReceivedCompoundPacket {
pub(crate) ctx: PacketContext,
pub(crate) stream_id: usize,
pub(crate) rtp_timestamp: Option<crate::Timestamp>,
pub(crate) raw: Bytes,
}
impl ReceivedCompoundPacket {
#[doc(hidden)]
pub fn dummy(rtp_timestamp: Option<crate::Timestamp>, data: &[u8]) -> Self {
Self {
ctx: PacketContext::dummy(),
stream_id: 0,
rtp_timestamp,
raw: Bytes::copy_from_slice(data),
}
}
pub(crate) fn validate(raw: &[u8]) -> Result<PacketRef<'_>, String> {
let (first_pkt, mut rest) = PacketRef::parse(raw)?;
let mut pkt = first_pkt;
loop {
if rest.is_empty() {
break;
} else if pkt.has_padding() {
return Err("padding on non-final packet within RTCP compound packet".to_owned());
}
(pkt, rest) = PacketRef::parse(rest)?;
}
Ok(first_pkt)
}
#[inline]
pub fn ctx(&self) -> &PacketContext {
&self.ctx
}
#[inline]
pub fn stream_id(&self) -> usize {
self.stream_id
}
#[inline]
pub fn rtp_timestamp(&self) -> Option<crate::Timestamp> {
self.rtp_timestamp
}
#[inline]
pub fn raw(&self) -> &[u8] {
&self.raw[..]
}
#[inline]
pub fn pkts(&self) -> impl Iterator<Item = PacketRef<'_>> {
CompoundPacketIterator(&self.raw[..])
}
}
impl std::fmt::Debug for ReceivedCompoundPacket {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReceivedCompoundPacket")
.field("ctx", &self.ctx)
.field("stream_id", &self.stream_id)
.field("rtp_timestamp", &self.rtp_timestamp)
.field("raw", &crate::hex::LimitedHex::new(&self.raw[..], 64))
.finish()
}
}
struct CompoundPacketIterator<'a>(&'a [u8]);
impl<'a> Iterator for CompoundPacketIterator<'a> {
type Item = PacketRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.0.is_empty() {
return None;
}
let (pkt, rest) =
PacketRef::parse(self.0).expect("failed to parse previously validated packet");
self.0 = rest;
Some(pkt)
}
}
#[non_exhaustive]
pub enum TypedPacketRef<'a> {
SenderReport(SenderReportRef<'a>),
ReceiverReport(ReceiverReportRef<'a>),
}
pub struct SenderReportRef<'a>(PacketRef<'a>);
impl<'a> SenderReportRef<'a> {
fn validate(pkt: PacketRef<'a>) -> Result<Self, String> {
let count = usize::from(pkt.count());
const HEADER_LEN: usize = 8;
const SENDER_INFO_LEN: usize = 20;
const REPORT_BLOCK_LEN: usize = 24;
let expected_len = HEADER_LEN + SENDER_INFO_LEN + (count * REPORT_BLOCK_LEN);
if pkt.payload_end < expected_len {
return Err(format!(
"RTCP SR has invalid count={} with unpadded_byte_len={}",
count, pkt.payload_end
));
}
Ok(Self(pkt))
}
pub fn ssrc(&self) -> u32 {
u32::from_be_bytes(self.0.buf[4..8].try_into().unwrap())
}
pub fn ntp_timestamp(&self) -> crate::NtpTimestamp {
crate::NtpTimestamp(u64::from_be_bytes(self.0.buf[8..16].try_into().unwrap()))
}
pub fn rtp_timestamp(&self) -> u32 {
u32::from_be_bytes(self.0.buf[16..20].try_into().unwrap())
}
}
impl<'a> std::ops::Deref for SenderReportRef<'a> {
type Target = PacketRef<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct ReceiverReportRef<'a>(PacketRef<'a>);
impl<'a> ReceiverReportRef<'a> {
fn validate(pkt: PacketRef<'a>) -> Result<Self, String> {
let count = usize::from(pkt.count());
const HEADER_LEN: usize = 8;
const REPORT_BLOCK_LEN: usize = 24;
let expected_len = HEADER_LEN + (count * REPORT_BLOCK_LEN);
if pkt.payload_end < expected_len {
return Err(format!(
"RTCP RR has invalid count={} with unpadded_byte_len={}",
count, pkt.payload_end
));
}
Ok(Self(pkt))
}
pub fn ssrc(&self) -> u32 {
u32::from_be_bytes(self.0.buf[4..8].try_into().unwrap())
}
}
impl<'a> std::ops::Deref for ReceiverReportRef<'a> {
type Target = PacketRef<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Copy, Clone)]
pub struct PacketRef<'a> {
buf: &'a [u8],
payload_end: usize,
}
const COMMON_HEADER_LEN: usize = 4;
impl<'a> PacketRef<'a> {
pub fn parse(buf: &'a [u8]) -> Result<(Self, &'a [u8]), String> {
if buf.len() < COMMON_HEADER_LEN {
return Err(format!(
"RTCP packets must be at least {} bytes; have only {}",
COMMON_HEADER_LEN,
buf.len()
));
}
let ver = buf[0] >> 6;
if ver != 2 {
return Err(format!("RTCP packets must be version 2; got {ver}"));
}
let raw_len = (u16::from(buf[2]) << 8) | u16::from(buf[3]);
let len = (usize::from(raw_len) + 1) * 4;
if buf.len() < len {
return Err(format!(
"RTCP packet header has length {} bytes; have only {}",
len,
buf.len()
));
}
let (this, rest) = buf.split_at(len);
let padding_bit = this[0] & 0b0010_0000;
if padding_bit != 0 {
if raw_len == 0 {
return Err("RTCP packet has invalid combination of padding and len=0".to_owned());
}
let padding_bytes = usize::from(this[len - 1]);
if padding_bytes == 0 || padding_bytes > len - COMMON_HEADER_LEN {
return Err(format!(
"RTCP packet of len {len} states invalid {padding_bytes} padding bytes"
));
}
Ok((
PacketRef {
buf: this,
payload_end: len - padding_bytes,
},
rest,
))
} else {
Ok((
PacketRef {
buf: this,
payload_end: len,
},
rest,
))
}
}
#[inline]
pub fn payload_type(&self) -> u8 {
self.buf[1]
}
pub fn as_typed(self) -> Result<Option<TypedPacketRef<'a>>, String> {
match self.payload_type() {
200 => Ok(Some(TypedPacketRef::SenderReport(
SenderReportRef::validate(self)?,
))),
201 => Ok(Some(TypedPacketRef::ReceiverReport(
ReceiverReportRef::validate(self)?,
))),
_ => Ok(None),
}
}
pub fn as_sender_report(self) -> Result<Option<SenderReportRef<'a>>, String> {
if self.payload_type() == 200 {
return Ok(Some(SenderReportRef::validate(self)?));
}
Ok(None)
}
#[inline]
pub fn has_padding(&self) -> bool {
(self.buf[0] & 0b0010_0000) != 0
}
#[inline]
pub fn count(&self) -> u8 {
self.buf[0] & 0b0001_1111
}
#[inline]
pub fn raw(&self) -> &[u8] {
self.buf
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dahua() {
let buf = b"\x80\xc8\x00\x06\x66\x42\x6a\xe1\
\xe4\x36\x2f\x99\xcc\xcc\xcc\xcc\
\x85\x2e\xf8\x07\x00\x2a\x43\x33\
\x2f\x4c\x34\x1d\
\x81\xca\x00\x04\x66\x42\x6a\xe1\
\x01\x06\x28\x6e\x6f\x6e\x65\x29\
\x00\x00\x00\x00";
let (pkt, buf) = PacketRef::parse(buf).unwrap();
let sr = pkt.as_sender_report().unwrap().unwrap();
assert_eq!(sr.ntp_timestamp(), crate::NtpTimestamp(0xe4362f99cccccccc));
assert_eq!(sr.rtp_timestamp(), 0x852ef807);
let (pkt, buf) = PacketRef::parse(buf).unwrap();
assert_eq!(pkt.payload_type(), 202);
assert_eq!(buf.len(), 0);
}
#[test]
fn padding() {
let buf = b"\xa7\x00\x00\x02asdf\x00\x00\x00\x04rest";
let (pkt, rest) = PacketRef::parse(buf).unwrap();
assert_eq!(pkt.count(), 7);
assert_eq!(&pkt.buf[4..pkt.payload_end], b"asdf");
assert_eq!(b"rest", rest);
}
}