use crate::feedback::FeedbackPacketType;
use crate::{Error, Result, RtpSsrc};
use bytes::{Buf, BufMut, Bytes, BytesMut};
pub const RTCP_HEADER_SIZE: usize = 4;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PliPacket {
pub sender_ssrc: RtpSsrc,
pub media_ssrc: RtpSsrc,
}
impl PliPacket {
pub fn new(sender_ssrc: RtpSsrc, media_ssrc: RtpSsrc) -> Self {
Self {
sender_ssrc,
media_ssrc,
}
}
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 8 {
return Err(Error::BufferTooSmall {
required: 8,
available: data.len(),
});
}
let mut buf = Bytes::copy_from_slice(data);
let sender_ssrc = buf.get_u32();
let media_ssrc = buf.get_u32();
Ok(Self {
sender_ssrc,
media_ssrc,
})
}
pub fn serialize(&self) -> Result<Bytes> {
let mut buf = BytesMut::with_capacity(12);
buf.put_u8(0x81); buf.put_u8(FeedbackPacketType::PayloadSpecificFeedback as u8);
buf.put_u16(2);
buf.put_u32(self.sender_ssrc);
buf.put_u32(self.media_ssrc);
Ok(buf.freeze())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FirPacket {
pub sender_ssrc: RtpSsrc,
pub media_ssrc: RtpSsrc,
pub sequence_number: u8,
}
impl FirPacket {
pub fn new(sender_ssrc: RtpSsrc, media_ssrc: RtpSsrc, sequence_number: u8) -> Self {
Self {
sender_ssrc,
media_ssrc,
sequence_number,
}
}
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 12 {
return Err(Error::BufferTooSmall {
required: 12,
available: data.len(),
});
}
let mut buf = Bytes::copy_from_slice(data);
let sender_ssrc = buf.get_u32();
let media_ssrc = buf.get_u32();
let sequence_number = buf.get_u8();
Ok(Self {
sender_ssrc,
media_ssrc,
sequence_number,
})
}
pub fn serialize(&self) -> Result<Bytes> {
let mut buf = BytesMut::with_capacity(16);
buf.put_u8(0x84); buf.put_u8(FeedbackPacketType::PayloadSpecificFeedback as u8);
buf.put_u16(3);
buf.put_u32(self.sender_ssrc);
buf.put_u32(self.media_ssrc);
buf.put_u8(self.sequence_number);
buf.put_u8(0); buf.put_u16(0);
Ok(buf.freeze())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SliEntry {
pub first: u16,
pub number: u16,
pub picture_id: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SliPacket {
pub sender_ssrc: RtpSsrc,
pub media_ssrc: RtpSsrc,
pub entries: Vec<SliEntry>,
}
impl SliPacket {
pub fn new(sender_ssrc: RtpSsrc, media_ssrc: RtpSsrc) -> Self {
Self {
sender_ssrc,
media_ssrc,
entries: Vec::new(),
}
}
pub fn add_entry(&mut self, first: u16, number: u16, picture_id: u8) {
self.entries.push(SliEntry {
first,
number,
picture_id,
});
}
pub fn serialize(&self) -> Result<Bytes> {
let payload_size = 8 + self.entries.len() * 4; let mut buf = BytesMut::with_capacity(4 + payload_size);
buf.put_u8(0x82); buf.put_u8(FeedbackPacketType::PayloadSpecificFeedback as u8);
buf.put_u16(((payload_size / 4) - 1) as u16);
buf.put_u32(self.sender_ssrc);
buf.put_u32(self.media_ssrc);
for entry in &self.entries {
buf.put_u16(entry.first);
buf.put_u16(entry.number);
buf.put_u8(entry.picture_id << 2);
buf.put_u8(0); buf.put_u16(0); }
Ok(buf.freeze())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TstoPacket {
pub sender_ssrc: RtpSsrc,
pub media_ssrc: RtpSsrc,
pub sequence_number: u8,
pub tradeoff_index: u8,
}
impl TstoPacket {
pub fn new(
sender_ssrc: RtpSsrc,
media_ssrc: RtpSsrc,
sequence_number: u8,
tradeoff_index: u8,
) -> Self {
Self {
sender_ssrc,
media_ssrc,
sequence_number,
tradeoff_index: tradeoff_index & 0x1F, }
}
pub fn serialize(&self) -> Result<Bytes> {
let mut buf = BytesMut::with_capacity(16);
buf.put_u8(0x85); buf.put_u8(FeedbackPacketType::PayloadSpecificFeedback as u8);
buf.put_u16(3);
buf.put_u32(self.sender_ssrc);
buf.put_u32(self.media_ssrc);
buf.put_u8(self.sequence_number);
buf.put_u8(0); buf.put_u8(self.tradeoff_index);
buf.put_u8(0);
Ok(buf.freeze())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RembPacket {
pub sender_ssrc: RtpSsrc,
pub media_ssrc: RtpSsrc,
pub bitrate_bps: u32,
pub ssrcs: Vec<RtpSsrc>,
}
impl RembPacket {
pub fn new(sender_ssrc: RtpSsrc, bitrate_bps: u32, ssrcs: Vec<RtpSsrc>) -> Self {
Self {
sender_ssrc,
media_ssrc: 0, bitrate_bps,
ssrcs,
}
}
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 16 {
return Err(Error::BufferTooSmall {
required: 16,
available: data.len(),
});
}
let mut buf = Bytes::copy_from_slice(data);
let sender_ssrc = buf.get_u32();
let media_ssrc = buf.get_u32();
let remb_id = buf.get_u32();
if remb_id != 0x52454D42 {
return Err(Error::RtcpError("Invalid REMB identifier".to_string()));
}
let num_ssrc_br = buf.get_u32();
let num_ssrcs = (num_ssrc_br >> 24) as u8;
let bitrate_bps = num_ssrc_br & 0x3FFFF;
let exp = (bitrate_bps >> 14) & 0x3F;
let mantissa = bitrate_bps & 0x3FFF;
let decoded_bitrate = mantissa << exp;
let mut ssrcs = Vec::with_capacity(num_ssrcs as usize);
for _ in 0..num_ssrcs {
if buf.remaining() < 4 {
break;
}
ssrcs.push(buf.get_u32());
}
Ok(Self {
sender_ssrc,
media_ssrc,
bitrate_bps: decoded_bitrate,
ssrcs,
})
}
pub fn serialize(&self) -> Result<Bytes> {
let payload_size = 16 + self.ssrcs.len() * 4; let mut buf = BytesMut::with_capacity(4 + payload_size);
buf.put_u8(0x8F); buf.put_u8(FeedbackPacketType::PayloadSpecificFeedback as u8);
buf.put_u16(((payload_size / 4) - 1) as u16);
buf.put_u32(self.sender_ssrc);
buf.put_u32(self.media_ssrc);
buf.put_u32(0x52454D42);
let (exp, mantissa) = Self::encode_bitrate(self.bitrate_bps);
let encoded_bitrate = ((exp as u32) << 14) | (mantissa as u32);
let num_ssrc_br = ((self.ssrcs.len() as u32) << 24) | encoded_bitrate;
buf.put_u32(num_ssrc_br);
for ssrc in &self.ssrcs {
buf.put_u32(*ssrc);
}
Ok(buf.freeze())
}
fn encode_bitrate(bitrate: u32) -> (u8, u16) {
if bitrate == 0 {
return (0, 0);
}
let mut exp = 0u8;
let mut mantissa = bitrate;
while mantissa > 0x3FFF && exp < 63 {
mantissa >>= 1;
exp += 1;
}
(exp, mantissa as u16)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TransportCcEntry {
pub sequence_number: u16,
pub receive_delta: Option<i16>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TransportCcPacket {
pub sender_ssrc: RtpSsrc,
pub media_ssrc: RtpSsrc,
pub base_sequence: u16,
pub packet_status_count: u16,
pub reference_time: u32,
pub feedback_count: u8,
pub entries: Vec<TransportCcEntry>,
}
impl TransportCcPacket {
pub fn new(
sender_ssrc: RtpSsrc,
base_sequence: u16,
reference_time: u32,
feedback_count: u8,
) -> Self {
Self {
sender_ssrc,
media_ssrc: 0, base_sequence,
packet_status_count: 0,
reference_time: reference_time & 0xFFFFFF, feedback_count,
entries: Vec::new(),
}
}
pub fn add_entry(&mut self, sequence_number: u16, receive_delta: Option<i16>) {
self.entries.push(TransportCcEntry {
sequence_number,
receive_delta,
});
self.packet_status_count = self.entries.len() as u16;
}
pub fn serialize(&self) -> Result<Bytes> {
let mut buf = BytesMut::with_capacity(64);
buf.put_u8(0x8F); buf.put_u8(FeedbackPacketType::GenericNack as u8);
let length_pos = buf.len();
buf.put_u16(0);
buf.put_u32(self.sender_ssrc);
buf.put_u32(self.media_ssrc);
buf.put_u16(self.base_sequence);
buf.put_u16(self.packet_status_count);
buf.put_u8((self.reference_time >> 16) as u8);
buf.put_u8((self.reference_time >> 8) as u8);
buf.put_u8(self.reference_time as u8);
buf.put_u8(self.feedback_count);
for entry in &self.entries {
if let Some(delta) = entry.receive_delta {
buf.put_i16(delta);
} else {
buf.put_u16(0); }
}
let total_length = buf.len();
let length_words = (total_length / 4) - 1;
buf[length_pos..length_pos + 2].copy_from_slice(&(length_words as u16).to_be_bytes());
Ok(buf.freeze())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FeedbackPacket {
Pli(PliPacket),
Fir(FirPacket),
Sli(SliPacket),
Tsto(TstoPacket),
Remb(RembPacket),
TransportCc(TransportCcPacket),
}
impl FeedbackPacket {
pub fn parse_from_rtcp(data: &[u8]) -> Result<Self> {
if data.len() < 4 {
return Err(Error::BufferTooSmall {
required: 4,
available: data.len(),
});
}
let first_byte = data[0];
let fmt = first_byte & 0x1F;
let packet_type = data[1];
match packet_type {
206 => {
match fmt {
1 => Ok(FeedbackPacket::Pli(PliPacket::parse(&data[4..])?)),
4 => Ok(FeedbackPacket::Fir(FirPacket::parse(&data[4..])?)),
15 => {
if data.len() >= 16 {
let remb_check = &data[12..16];
if remb_check == b"REMB" {
return Ok(FeedbackPacket::Remb(RembPacket::parse(&data[4..])?));
}
}
Err(Error::RtcpError("Unsupported ALF format".to_string()))
}
_ => Err(Error::RtcpError(format!(
"Unsupported feedback format: {}",
fmt
))),
}
}
205 => {
if fmt == 15 {
Err(Error::RtcpError(
"Transport CC parsing not fully implemented".to_string(),
))
} else {
Err(Error::RtcpError(format!(
"Unsupported generic feedback format: {}",
fmt
)))
}
}
_ => Err(Error::RtcpError(format!(
"Not a feedback packet type: {}",
packet_type
))),
}
}
pub fn serialize(&self) -> Result<Bytes> {
match self {
FeedbackPacket::Pli(pli) => pli.serialize(),
FeedbackPacket::Fir(fir) => fir.serialize(),
FeedbackPacket::Sli(sli) => sli.serialize(),
FeedbackPacket::Tsto(tsto) => tsto.serialize(),
FeedbackPacket::Remb(remb) => remb.serialize(),
FeedbackPacket::TransportCc(transport_cc) => transport_cc.serialize(),
}
}
pub fn sender_ssrc(&self) -> RtpSsrc {
match self {
FeedbackPacket::Pli(pli) => pli.sender_ssrc,
FeedbackPacket::Fir(fir) => fir.sender_ssrc,
FeedbackPacket::Sli(sli) => sli.sender_ssrc,
FeedbackPacket::Tsto(tsto) => tsto.sender_ssrc,
FeedbackPacket::Remb(remb) => remb.sender_ssrc,
FeedbackPacket::TransportCc(transport_cc) => transport_cc.sender_ssrc,
}
}
pub fn media_ssrc(&self) -> RtpSsrc {
match self {
FeedbackPacket::Pli(pli) => pli.media_ssrc,
FeedbackPacket::Fir(fir) => fir.media_ssrc,
FeedbackPacket::Sli(sli) => sli.media_ssrc,
FeedbackPacket::Tsto(tsto) => tsto.media_ssrc,
FeedbackPacket::Remb(remb) => remb.media_ssrc,
FeedbackPacket::TransportCc(transport_cc) => transport_cc.media_ssrc,
}
}
}