use bytes::{Buf, BufMut, Bytes, BytesMut};
use crate::error::Error;
use crate::Result;
pub const RTCP_VERSION: u8 = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RtcpPacketType {
SenderReport = 200,
ReceiverReport = 201,
SourceDescription = 202,
Goodbye = 203,
ApplicationDefined = 204,
ExtendedReport = 207,
}
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),
207 => Ok(RtcpPacketType::ExtendedReport),
_ => Err(Error::RtcpError(format!(
"Unknown RTCP packet type: {}",
value
))),
}
}
}
mod app;
mod bye;
mod compound;
mod ntp;
mod receiver_report;
mod report_block;
mod sdes;
mod sender_report;
mod xr;
pub use app::RtcpApplicationDefined;
pub use bye::RtcpGoodbye;
pub use compound::RtcpCompoundPacket;
pub use ntp::NtpTimestamp;
pub use receiver_report::RtcpReceiverReport;
pub use report_block::RtcpReportBlock;
pub use sdes::{RtcpSdesChunk, RtcpSdesItem, RtcpSdesItemType, RtcpSourceDescription};
pub use sender_report::RtcpSenderReport;
pub use xr::{
ReceiverReferenceTimeBlock, RtcpExtendedReport, RtcpXrBlock, RtcpXrBlockType, VoipMetricsBlock,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RtcpPacket {
SenderReport(RtcpSenderReport),
ReceiverReport(RtcpReceiverReport),
SourceDescription(RtcpSourceDescription),
Goodbye(RtcpGoodbye),
ApplicationDefined(RtcpApplicationDefined),
ExtendedReport(RtcpExtendedReport),
}
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 => Ok(RtcpPacket::SenderReport(
sender_report::parse_sender_report(&mut buf, report_count)?,
)),
RtcpPacketType::ReceiverReport => Ok(RtcpPacket::ReceiverReport(
receiver_report::parse_receiver_report(&mut buf, report_count)?,
)),
RtcpPacketType::SourceDescription => {
Ok(RtcpPacket::SourceDescription(RtcpSourceDescription {
chunks: Vec::new(),
}))
}
RtcpPacketType::Goodbye => {
Ok(RtcpPacket::Goodbye(bye::parse_bye(&mut buf, report_count)?))
}
RtcpPacketType::ApplicationDefined => {
Ok(RtcpPacket::ApplicationDefined(app::parse_app(&mut buf)?))
}
RtcpPacketType::ExtendedReport => {
Ok(RtcpPacket::ExtendedReport(xr::parse_xr(&mut buf)?))
}
}
}
pub fn serialize(&self) -> Result<Bytes> {
let mut buf = BytesMut::new();
match self {
RtcpPacket::SenderReport(sr) => {
let sr_content = sender_report::serialize_sender_report(sr)?;
let content_size = sr_content.len();
let words = (content_size + 4) / 4; let length = words - 1;
let first_byte =
(RTCP_VERSION << 6) | (0 << 5) | (sr.report_blocks.len() as u8 & 0x1F);
buf.put_u8(first_byte);
buf.put_u8(RtcpPacketType::SenderReport as u8);
buf.put_u16(length as u16);
buf.extend_from_slice(&sr_content);
let padding_bytes = (4 - (buf.len() % 4)) % 4;
for _ in 0..padding_bytes {
buf.put_u8(0);
}
}
RtcpPacket::ReceiverReport(rr) => {
let rr_content = receiver_report::serialize_receiver_report(rr)?;
let content_size = rr_content.len();
let words = (content_size + 4) / 4; let length = words - 1;
let first_byte =
(RTCP_VERSION << 6) | (0 << 5) | (rr.report_blocks.len() as u8 & 0x1F);
buf.put_u8(first_byte);
buf.put_u8(RtcpPacketType::ReceiverReport as u8);
buf.put_u16(length as u16);
buf.extend_from_slice(&rr_content);
let padding_bytes = (4 - (buf.len() % 4)) % 4;
for _ in 0..padding_bytes {
buf.put_u8(0);
}
}
RtcpPacket::SourceDescription(sdes) => {
let sdes_content = sdes::serialize_sdes(sdes)?;
let content_size = sdes_content.len();
let words = (content_size + 4) / 4; let length = words - 1;
let first_byte = (RTCP_VERSION << 6) | (0 << 5) | (sdes.chunks.len() as u8 & 0x1F);
buf.put_u8(first_byte);
buf.put_u8(RtcpPacketType::SourceDescription as u8);
buf.put_u16(length as u16);
buf.extend_from_slice(&sdes_content);
let padding_bytes = (4 - (buf.len() % 4)) % 4;
for _ in 0..padding_bytes {
buf.put_u8(0);
}
}
RtcpPacket::Goodbye(bye) => {
let bye_content = bye.serialize()?;
let content_size = bye_content.len();
let words = (content_size + 4) / 4; let length = words - 1;
let first_byte = (RTCP_VERSION << 6) | (0 << 5) | (bye.sources.len() as u8 & 0x1F);
buf.put_u8(first_byte);
buf.put_u8(RtcpPacketType::Goodbye as u8);
buf.put_u16(length as u16);
buf.extend_from_slice(&bye_content);
let padding_bytes = (4 - (buf.len() % 4)) % 4;
for _ in 0..padding_bytes {
buf.put_u8(0);
}
}
RtcpPacket::ApplicationDefined(app) => {
let app_content = app.serialize()?;
let content_size = app_content.len();
let words = (content_size + 4) / 4; let length = words - 1;
let first_byte = (RTCP_VERSION << 6) | (0 << 5) | 0;
buf.put_u8(first_byte);
buf.put_u8(RtcpPacketType::ApplicationDefined as u8);
buf.put_u16(length as u16);
buf.extend_from_slice(&app_content);
let padding_bytes = (4 - (buf.len() % 4)) % 4;
for _ in 0..padding_bytes {
buf.put_u8(0);
}
}
RtcpPacket::ExtendedReport(xr) => {
let xr_content = xr.serialize()?;
let content_size = xr_content.len();
let words = (content_size + 4) / 4; let length = words - 1;
let first_byte = (RTCP_VERSION << 6) | (0 << 5) | 0;
buf.put_u8(first_byte);
buf.put_u8(RtcpPacketType::ExtendedReport as u8);
buf.put_u16(length as u16);
buf.extend_from_slice(&xr_content);
let padding_bytes = (4 - (buf.len() % 4)) % 4;
for _ in 0..padding_bytes {
buf.put_u8(0);
}
}
}
Ok(buf.freeze())
}
pub fn packet_type(&self) -> RtcpPacketType {
match self {
RtcpPacket::SenderReport(_) => RtcpPacketType::SenderReport,
RtcpPacket::ReceiverReport(_) => RtcpPacketType::ReceiverReport,
RtcpPacket::SourceDescription(_) => RtcpPacketType::SourceDescription,
RtcpPacket::Goodbye(_) => RtcpPacketType::Goodbye,
RtcpPacket::ApplicationDefined(_) => RtcpPacketType::ApplicationDefined,
RtcpPacket::ExtendedReport(_) => RtcpPacketType::ExtendedReport,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rtcp_packet_type_conversion() {
assert_eq!(
RtcpPacketType::try_from(200).unwrap(),
RtcpPacketType::SenderReport
);
assert_eq!(
RtcpPacketType::try_from(201).unwrap(),
RtcpPacketType::ReceiverReport
);
assert_eq!(
RtcpPacketType::try_from(202).unwrap(),
RtcpPacketType::SourceDescription
);
assert_eq!(
RtcpPacketType::try_from(203).unwrap(),
RtcpPacketType::Goodbye
);
assert_eq!(
RtcpPacketType::try_from(204).unwrap(),
RtcpPacketType::ApplicationDefined
);
assert_eq!(
RtcpPacketType::try_from(207).unwrap(),
RtcpPacketType::ExtendedReport
);
assert!(RtcpPacketType::try_from(100).is_err());
}
}