use crate::rtp::SSRC; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("packet truncated")] Truncated, #[error("unsupported version")] Version, } pub struct RtcpPacket<'a> { pub parts: Vec>, } pub enum RtcpPart<'a> { SenderReport(SenderReport<'a>), ReceiverReport(ReceiverReport), SourceDescription(SourceDescription), Bye(Bye<'a>), Application(Application<'a>), } pub struct SenderReport<'a> { pub ssrc: SSRC, pub sender_info: SenderInfo, pub reports: Vec, pub extension: &'a [u8], } pub struct ReceiverReport { pub sender_ssrc: SSRC, pub reports: Vec, } pub struct SourceDescription { pub sources: Vec<(SSRC, Vec<(SdesType, String)>)>, } #[derive(Debug, Clone, Copy)] pub enum SdesType { CanonicalName = 1, Name = 2, Email = 3, Phone = 4, Location = 5, Tool = 6, Notice = 7, PrivateExtension = 8, } pub struct Bye<'a> { pub ssrcs: Vec, pub reason: &'a [u8], } pub struct Application<'a> { pub subtype: u8, pub name: [u8; 4], pub ssrc: SSRC, pub data: &'a [u8], } pub struct SenderInfo { pub ntp_ts: u64, pub rtp_ts: u32, pub packet_count: u32, pub octet_count: u32, } pub struct ReportBlock { pub ssrc: SSRC, pub fraction_lost: u8, pub cumulative_packets_lost: u32, pub ext_max_seq_num_recv: u32, pub interarrical_jitter: u32, pub lsr: u32, pub dlsr: u32, } impl<'a> RtcpPacket<'a> { pub fn parse(mut packet: &'a [u8]) -> Result, Error> { let mut parts = Vec::with_capacity(2); while packet.len() > 0 { if packet.len() < 4 { return Err(Error::Truncated); } let version = (packet[0] & 0b11000000) >> 6; if !matches!(version, 1 | 2) { return Err(Error::Version); } let padding = (packet[0] & 0b00100000) != 0; let num_reports = (packet[0] & 0b00011111) >> 0; let packet_type = packet[1]; let length = u16::from_be_bytes([packet[2], packet[3]]); match packet_type { 200 => { // sender seport if packet.len() < 28 + 24 * num_reports as usize { return Err(Error::Truncated); } let ssrc = SSRC(u32::from_be_bytes(packet[4..8].try_into().unwrap())); let sender_info = SenderInfo::parse(packet[8..28].try_into().unwrap()); let mut reports = Vec::with_capacity(num_reports as usize); for n in 0..num_reports as usize { reports.push(ReportBlock::parse( packet[28 + 24 * n..28 + 24 * (n + 1)].try_into().unwrap(), )); } let extension = &packet[28 + 24 * num_reports as usize..]; parts.push(RtcpPart::SenderReport(SenderReport { reports, sender_info, ssrc, extension, })) } 201 => { // receiver report } 202 => { // source description } 203 => { // bye } 204 => { // application defined } _ => {} } packet = &packet[length as usize..]; } Ok(Self { parts }) } pub fn write(&self, out: &mut Vec) { for part in &self.parts { let version = 2; let padding = false; match part { RtcpPart::SenderReport(sender_report) => { out.push( version << 6 | (padding as u8) << 5 | sender_report.reports.len() as u8, ); out.push(200); todo!() } RtcpPart::ReceiverReport(receiver_report) => { out.push( version << 6 | (padding as u8) << 5 | receiver_report.reports.len() as u8, ); out.push(201); out.extend(((receiver_report.reports.len() * 6 + 1) as u16).to_be_bytes()); out.extend(receiver_report.sender_ssrc.0.to_be_bytes()); for r in &receiver_report.reports { r.write(out); } } RtcpPart::SourceDescription(source_description) => { out.push( version << 6 | (padding as u8) << 5 | source_description.sources.len() as u8, ); out.push(202); let mut buf = Vec::new(); for (ssrc, items) in &source_description.sources { buf.extend(ssrc.0.to_be_bytes()); for (ty, name) in items { buf.push(*ty as u8); buf.push(name.len() as u8); buf.extend(name.as_bytes()); } while buf.len() % 4 != 0 { buf.push(0); } } out.extend(((buf.len() / 4) as u16).to_be_bytes()); out.extend(buf); } RtcpPart::Bye(bye) => todo!(), RtcpPart::Application(application) => { assert!(application.data.len() % 4 == 0, "todo"); out.push(version << 6 | (padding as u8) << 5 | application.subtype); out.push(204); out.extend(((application.data.len() / 4 + 2) as u16).to_be_bytes()); out.extend(application.ssrc.0.to_be_bytes()); out.extend(application.name); out.extend(application.data); } } } } } impl SenderInfo { pub const SIZE: usize = 5 * 4; pub fn parse(packet: [u8; 5 * 4]) -> SenderInfo { Self { ntp_ts: u64::from_be_bytes(packet[0..8].try_into().unwrap()), rtp_ts: u32::from_be_bytes(packet[8..12].try_into().unwrap()), packet_count: u32::from_be_bytes(packet[12..16].try_into().unwrap()), octet_count: u32::from_be_bytes(packet[16..20].try_into().unwrap()), } } pub fn write(&self, out: &mut Vec) { out.extend(self.ntp_ts.to_be_bytes()); out.extend(self.rtp_ts.to_be_bytes()); out.extend(self.packet_count.to_be_bytes()); out.extend(self.octet_count.to_be_bytes()); } } impl ReportBlock { pub fn parse(packet: [u8; 6 * 4]) -> ReportBlock { Self { ssrc: SSRC(u32::from_be_bytes(packet[0..4].try_into().unwrap())), fraction_lost: packet[4], cumulative_packets_lost: u32::from_be_bytes([0, packet[5], packet[6], packet[7]]), ext_max_seq_num_recv: u32::from_be_bytes(packet[8..12].try_into().unwrap()), interarrical_jitter: u32::from_be_bytes(packet[12..16].try_into().unwrap()), lsr: u32::from_be_bytes(packet[16..20].try_into().unwrap()), dlsr: u32::from_be_bytes(packet[20..24].try_into().unwrap()), } } pub fn write(&self, out: &mut Vec) { out.extend(self.ssrc.0.to_be_bytes()); out.push(self.fraction_lost); out.extend(&self.cumulative_packets_lost.to_be_bytes()[1..]); out.extend(self.ext_max_seq_num_recv.to_be_bytes()); out.extend(self.interarrical_jitter.to_be_bytes()); out.extend(self.lsr.to_be_bytes()); out.extend(self.dlsr.to_be_bytes()); } }