1ffe74184SMartin Algesten use crate::error::Error; 2ffe74184SMartin Algesten use util::marshal::{Marshal, MarshalSize, Unmarshal}; 3ffe74184SMartin Algesten 4ffe74184SMartin Algesten use bytes::{Buf, BufMut}; 5ffe74184SMartin Algesten 6ffe74184SMartin Algesten /// PacketType specifies the type of an RTCP packet 7ffe74184SMartin Algesten /// RTCP packet types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-4 8*97921129Syngrtc #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] 9ffe74184SMartin Algesten #[repr(u8)] 10ffe74184SMartin Algesten pub enum PacketType { 11*97921129Syngrtc #[default] 12ffe74184SMartin Algesten Unsupported = 0, 13ffe74184SMartin Algesten SenderReport = 200, // RFC 3550, 6.4.1 14ffe74184SMartin Algesten ReceiverReport = 201, // RFC 3550, 6.4.2 15ffe74184SMartin Algesten SourceDescription = 202, // RFC 3550, 6.5 16ffe74184SMartin Algesten Goodbye = 203, // RFC 3550, 6.6 17ffe74184SMartin Algesten ApplicationDefined = 204, // RFC 3550, 6.7 (unimplemented) 18ffe74184SMartin Algesten TransportSpecificFeedback = 205, // RFC 4585, 6051 19ffe74184SMartin Algesten PayloadSpecificFeedback = 206, // RFC 4585, 6.3 20ffe74184SMartin Algesten ExtendedReport = 207, // RFC 3611 21ffe74184SMartin Algesten } 22ffe74184SMartin Algesten 23ffe74184SMartin Algesten /// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here 24ffe74184SMartin Algesten pub const FORMAT_SLI: u8 = 2; 25ffe74184SMartin Algesten /// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here 26ffe74184SMartin Algesten pub const FORMAT_PLI: u8 = 1; 27ffe74184SMartin Algesten /// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here 28ffe74184SMartin Algesten pub const FORMAT_FIR: u8 = 4; 29ffe74184SMartin Algesten /// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here 30ffe74184SMartin Algesten pub const FORMAT_TLN: u8 = 1; 31ffe74184SMartin Algesten /// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here 32ffe74184SMartin Algesten pub const FORMAT_RRR: u8 = 5; 33ffe74184SMartin Algesten /// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here 34ffe74184SMartin Algesten pub const FORMAT_REMB: u8 = 15; 35ffe74184SMartin Algesten /// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here. 36ffe74184SMartin Algesten /// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5 37ffe74184SMartin Algesten pub const FORMAT_TCC: u8 = 15; 38ffe74184SMartin Algesten 39ffe74184SMartin Algesten impl std::fmt::Display for PacketType { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result40ffe74184SMartin Algesten fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 41ffe74184SMartin Algesten let s = match self { 42ffe74184SMartin Algesten PacketType::Unsupported => "Unsupported", 43ffe74184SMartin Algesten PacketType::SenderReport => "SR", 44ffe74184SMartin Algesten PacketType::ReceiverReport => "RR", 45ffe74184SMartin Algesten PacketType::SourceDescription => "SDES", 46ffe74184SMartin Algesten PacketType::Goodbye => "BYE", 47ffe74184SMartin Algesten PacketType::ApplicationDefined => "APP", 48ffe74184SMartin Algesten PacketType::TransportSpecificFeedback => "TSFB", 49ffe74184SMartin Algesten PacketType::PayloadSpecificFeedback => "PSFB", 50ffe74184SMartin Algesten PacketType::ExtendedReport => "XR", 51ffe74184SMartin Algesten }; 525d8fe953SJoão Oliveira write!(f, "{s}") 53ffe74184SMartin Algesten } 54ffe74184SMartin Algesten } 55ffe74184SMartin Algesten 56ffe74184SMartin Algesten impl From<u8> for PacketType { from(b: u8) -> Self57ffe74184SMartin Algesten fn from(b: u8) -> Self { 58ffe74184SMartin Algesten match b { 59ffe74184SMartin Algesten 200 => PacketType::SenderReport, // RFC 3550, 6.4.1 60ffe74184SMartin Algesten 201 => PacketType::ReceiverReport, // RFC 3550, 6.4.2 61ffe74184SMartin Algesten 202 => PacketType::SourceDescription, // RFC 3550, 6.5 62ffe74184SMartin Algesten 203 => PacketType::Goodbye, // RFC 3550, 6.6 63ffe74184SMartin Algesten 204 => PacketType::ApplicationDefined, // RFC 3550, 6.7 (unimplemented) 64ffe74184SMartin Algesten 205 => PacketType::TransportSpecificFeedback, // RFC 4585, 6051 65ffe74184SMartin Algesten 206 => PacketType::PayloadSpecificFeedback, // RFC 4585, 6.3 66ffe74184SMartin Algesten 207 => PacketType::ExtendedReport, // RFC 3611 67ffe74184SMartin Algesten _ => PacketType::Unsupported, 68ffe74184SMartin Algesten } 69ffe74184SMartin Algesten } 70ffe74184SMartin Algesten } 71ffe74184SMartin Algesten 72ffe74184SMartin Algesten pub const RTP_VERSION: u8 = 2; 73ffe74184SMartin Algesten pub const VERSION_SHIFT: u8 = 6; 74ffe74184SMartin Algesten pub const VERSION_MASK: u8 = 0x3; 75ffe74184SMartin Algesten pub const PADDING_SHIFT: u8 = 5; 76ffe74184SMartin Algesten pub const PADDING_MASK: u8 = 0x1; 77ffe74184SMartin Algesten pub const COUNT_SHIFT: u8 = 0; 78ffe74184SMartin Algesten pub const COUNT_MASK: u8 = 0x1f; 79ffe74184SMartin Algesten 80ffe74184SMartin Algesten pub const HEADER_LENGTH: usize = 4; 81ffe74184SMartin Algesten pub const COUNT_MAX: usize = (1 << 5) - 1; 82ffe74184SMartin Algesten pub const SSRC_LENGTH: usize = 4; 83ffe74184SMartin Algesten pub const SDES_MAX_OCTET_COUNT: usize = (1 << 8) - 1; 84ffe74184SMartin Algesten 85ffe74184SMartin Algesten /// A Header is the common header shared by all RTCP packets 86ffe74184SMartin Algesten #[derive(Debug, PartialEq, Eq, Default, Clone)] 87ffe74184SMartin Algesten pub struct Header { 88ffe74184SMartin Algesten /// If the padding bit is set, this individual RTCP packet contains 89ffe74184SMartin Algesten /// some additional padding octets at the end which are not part of 90ffe74184SMartin Algesten /// the control information but are included in the length field. 91ffe74184SMartin Algesten pub padding: bool, 92ffe74184SMartin Algesten /// The number of reception reports, sources contained or FMT in this packet (depending on the Type) 93ffe74184SMartin Algesten pub count: u8, 94ffe74184SMartin Algesten /// The RTCP packet type for this packet 95ffe74184SMartin Algesten pub packet_type: PacketType, 96ffe74184SMartin Algesten /// The length of this RTCP packet in 32-bit words minus one, 97ffe74184SMartin Algesten /// including the header and any padding. 98ffe74184SMartin Algesten pub length: u16, 99ffe74184SMartin Algesten } 100ffe74184SMartin Algesten 101ffe74184SMartin Algesten /// Marshal encodes the Header in binary 102ffe74184SMartin Algesten impl MarshalSize for Header { marshal_size(&self) -> usize103ffe74184SMartin Algesten fn marshal_size(&self) -> usize { 104ffe74184SMartin Algesten HEADER_LENGTH 105ffe74184SMartin Algesten } 106ffe74184SMartin Algesten } 107ffe74184SMartin Algesten 108ffe74184SMartin Algesten impl Marshal for Header { marshal_to(&self, mut buf: &mut [u8]) -> Result<usize, util::Error>109ffe74184SMartin Algesten fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize, util::Error> { 110ffe74184SMartin Algesten if self.count > 31 { 111ffe74184SMartin Algesten return Err(Error::InvalidHeader.into()); 112ffe74184SMartin Algesten } 113ffe74184SMartin Algesten if buf.remaining_mut() < HEADER_LENGTH { 114ffe74184SMartin Algesten return Err(Error::BufferTooShort.into()); 115ffe74184SMartin Algesten } 116ffe74184SMartin Algesten 117ffe74184SMartin Algesten /* 118ffe74184SMartin Algesten * 0 1 2 3 119ffe74184SMartin Algesten * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 120ffe74184SMartin Algesten * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 121ffe74184SMartin Algesten * |V=2|P| RC | PT=SR=200 | length | 122ffe74184SMartin Algesten * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 123ffe74184SMartin Algesten */ 124ffe74184SMartin Algesten let b0 = (RTP_VERSION << VERSION_SHIFT) 125ffe74184SMartin Algesten | ((self.padding as u8) << PADDING_SHIFT) 126ffe74184SMartin Algesten | (self.count << COUNT_SHIFT); 127ffe74184SMartin Algesten 128ffe74184SMartin Algesten buf.put_u8(b0); 129ffe74184SMartin Algesten buf.put_u8(self.packet_type as u8); 130ffe74184SMartin Algesten buf.put_u16(self.length); 131ffe74184SMartin Algesten 132ffe74184SMartin Algesten Ok(HEADER_LENGTH) 133ffe74184SMartin Algesten } 134ffe74184SMartin Algesten } 135ffe74184SMartin Algesten 136ffe74184SMartin Algesten impl Unmarshal for Header { 137ffe74184SMartin Algesten /// Unmarshal decodes the Header from binary unmarshal<B>(raw_packet: &mut B) -> Result<Self, util::Error> where Self: Sized, B: Buf,138ffe74184SMartin Algesten fn unmarshal<B>(raw_packet: &mut B) -> Result<Self, util::Error> 139ffe74184SMartin Algesten where 140ffe74184SMartin Algesten Self: Sized, 141ffe74184SMartin Algesten B: Buf, 142ffe74184SMartin Algesten { 143ffe74184SMartin Algesten if raw_packet.remaining() < HEADER_LENGTH { 144ffe74184SMartin Algesten return Err(Error::PacketTooShort.into()); 145ffe74184SMartin Algesten } 146ffe74184SMartin Algesten 147ffe74184SMartin Algesten /* 148ffe74184SMartin Algesten * 0 1 2 3 149ffe74184SMartin Algesten * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 150ffe74184SMartin Algesten * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 151ffe74184SMartin Algesten * |V=2|P| RC | PT | length | 152ffe74184SMartin Algesten * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 153ffe74184SMartin Algesten */ 154ffe74184SMartin Algesten let b0 = raw_packet.get_u8(); 155ffe74184SMartin Algesten let version = (b0 >> VERSION_SHIFT) & VERSION_MASK; 156ffe74184SMartin Algesten if version != RTP_VERSION { 157ffe74184SMartin Algesten return Err(Error::BadVersion.into()); 158ffe74184SMartin Algesten } 159ffe74184SMartin Algesten 160ffe74184SMartin Algesten let padding = ((b0 >> PADDING_SHIFT) & PADDING_MASK) > 0; 161ffe74184SMartin Algesten let count = (b0 >> COUNT_SHIFT) & COUNT_MASK; 162ffe74184SMartin Algesten let packet_type = PacketType::from(raw_packet.get_u8()); 163ffe74184SMartin Algesten let length = raw_packet.get_u16(); 164ffe74184SMartin Algesten 165ffe74184SMartin Algesten Ok(Header { 166ffe74184SMartin Algesten padding, 167ffe74184SMartin Algesten count, 168ffe74184SMartin Algesten packet_type, 169ffe74184SMartin Algesten length, 170ffe74184SMartin Algesten }) 171ffe74184SMartin Algesten } 172ffe74184SMartin Algesten } 173ffe74184SMartin Algesten 174ffe74184SMartin Algesten #[cfg(test)] 175ffe74184SMartin Algesten mod test { 176ffe74184SMartin Algesten use super::*; 177ffe74184SMartin Algesten use bytes::Bytes; 178ffe74184SMartin Algesten 179ffe74184SMartin Algesten #[test] test_header_unmarshal()180ffe74184SMartin Algesten fn test_header_unmarshal() { 181ffe74184SMartin Algesten let tests = vec![ 182ffe74184SMartin Algesten ( 183ffe74184SMartin Algesten "valid", 184ffe74184SMartin Algesten Bytes::from_static(&[ 185ffe74184SMartin Algesten // v=2, p=0, count=1, RR, len=7 186ffe74184SMartin Algesten 0x81u8, 0xc9, 0x00, 0x07, 187ffe74184SMartin Algesten ]), 188ffe74184SMartin Algesten Header { 189ffe74184SMartin Algesten padding: false, 190ffe74184SMartin Algesten count: 1, 191ffe74184SMartin Algesten packet_type: PacketType::ReceiverReport, 192ffe74184SMartin Algesten length: 7, 193ffe74184SMartin Algesten }, 194ffe74184SMartin Algesten None, 195ffe74184SMartin Algesten ), 196ffe74184SMartin Algesten ( 197ffe74184SMartin Algesten "also valid", 198ffe74184SMartin Algesten Bytes::from_static(&[ 199ffe74184SMartin Algesten // v=2, p=1, count=1, BYE, len=7 200ffe74184SMartin Algesten 0xa1, 0xcc, 0x00, 0x07, 201ffe74184SMartin Algesten ]), 202ffe74184SMartin Algesten Header { 203ffe74184SMartin Algesten padding: true, 204ffe74184SMartin Algesten count: 1, 205ffe74184SMartin Algesten packet_type: PacketType::ApplicationDefined, 206ffe74184SMartin Algesten length: 7, 207ffe74184SMartin Algesten }, 208ffe74184SMartin Algesten None, 209ffe74184SMartin Algesten ), 210ffe74184SMartin Algesten ( 211ffe74184SMartin Algesten "bad version", 212ffe74184SMartin Algesten Bytes::from_static(&[ 213ffe74184SMartin Algesten // v=0, p=0, count=0, RR, len=4 214ffe74184SMartin Algesten 0x00, 0xc9, 0x00, 0x04, 215ffe74184SMartin Algesten ]), 216ffe74184SMartin Algesten Header { 217ffe74184SMartin Algesten padding: false, 218ffe74184SMartin Algesten count: 0, 219ffe74184SMartin Algesten packet_type: PacketType::Unsupported, 220ffe74184SMartin Algesten length: 0, 221ffe74184SMartin Algesten }, 222ffe74184SMartin Algesten Some(Error::BadVersion), 223ffe74184SMartin Algesten ), 224ffe74184SMartin Algesten ]; 225ffe74184SMartin Algesten 226ffe74184SMartin Algesten for (name, data, want, want_error) in tests { 227ffe74184SMartin Algesten let buf = &mut data.clone(); 228ffe74184SMartin Algesten let got = Header::unmarshal(buf); 229ffe74184SMartin Algesten 230ffe74184SMartin Algesten assert_eq!( 231ffe74184SMartin Algesten got.is_err(), 232ffe74184SMartin Algesten want_error.is_some(), 2335d8fe953SJoão Oliveira "Unmarshal {name}: err = {got:?}, want {want_error:?}" 234ffe74184SMartin Algesten ); 235ffe74184SMartin Algesten 23683f2d1bbSVincent Esche if let Some(want_error) = want_error { 237ffe74184SMartin Algesten let got_err = got.err().unwrap(); 238ffe74184SMartin Algesten assert_eq!( 23983f2d1bbSVincent Esche want_error, got_err, 2405d8fe953SJoão Oliveira "Unmarshal {name}: err = {got_err:?}, want {want_error:?}", 241ffe74184SMartin Algesten ); 242ffe74184SMartin Algesten } else { 243ffe74184SMartin Algesten let actual = got.unwrap(); 244ffe74184SMartin Algesten assert_eq!( 245ffe74184SMartin Algesten actual, want, 2465d8fe953SJoão Oliveira "Unmarshal {name}: got {actual:?}, want {want:?}" 247ffe74184SMartin Algesten ); 248ffe74184SMartin Algesten } 249ffe74184SMartin Algesten } 250ffe74184SMartin Algesten } 251ffe74184SMartin Algesten 252ffe74184SMartin Algesten #[test] test_header_roundtrip()253ffe74184SMartin Algesten fn test_header_roundtrip() { 254ffe74184SMartin Algesten let tests = vec![ 255ffe74184SMartin Algesten ( 256ffe74184SMartin Algesten "valid", 257ffe74184SMartin Algesten Header { 258ffe74184SMartin Algesten padding: true, 259ffe74184SMartin Algesten count: 31, 260ffe74184SMartin Algesten packet_type: PacketType::SenderReport, 261ffe74184SMartin Algesten length: 4, 262ffe74184SMartin Algesten }, 263ffe74184SMartin Algesten None, 264ffe74184SMartin Algesten ), 265ffe74184SMartin Algesten ( 266ffe74184SMartin Algesten "also valid", 267ffe74184SMartin Algesten Header { 268ffe74184SMartin Algesten padding: false, 269ffe74184SMartin Algesten count: 28, 270ffe74184SMartin Algesten packet_type: PacketType::ReceiverReport, 271ffe74184SMartin Algesten length: 65535, 272ffe74184SMartin Algesten }, 273ffe74184SMartin Algesten None, 274ffe74184SMartin Algesten ), 275ffe74184SMartin Algesten ( 276ffe74184SMartin Algesten "invalid count", 277ffe74184SMartin Algesten Header { 278ffe74184SMartin Algesten padding: false, 279ffe74184SMartin Algesten count: 40, 280ffe74184SMartin Algesten packet_type: PacketType::Unsupported, 281ffe74184SMartin Algesten length: 0, 282ffe74184SMartin Algesten }, 283ffe74184SMartin Algesten Some(Error::InvalidHeader), 284ffe74184SMartin Algesten ), 285ffe74184SMartin Algesten ]; 286ffe74184SMartin Algesten 287ffe74184SMartin Algesten for (name, want, want_error) in tests { 288ffe74184SMartin Algesten let got = want.marshal(); 289ffe74184SMartin Algesten 290ffe74184SMartin Algesten assert_eq!( 291ffe74184SMartin Algesten got.is_ok(), 292ffe74184SMartin Algesten want_error.is_none(), 2935d8fe953SJoão Oliveira "Marshal {name}: err = {got:?}, want {want_error:?}" 294ffe74184SMartin Algesten ); 295ffe74184SMartin Algesten 296ffe74184SMartin Algesten if let Some(err) = want_error { 297ffe74184SMartin Algesten let got_err = got.err().unwrap(); 298ffe74184SMartin Algesten assert_eq!( 299ffe74184SMartin Algesten err, got_err, 3005d8fe953SJoão Oliveira "Unmarshal {name} rr: err = {got_err:?}, want {err:?}", 301ffe74184SMartin Algesten ); 302ffe74184SMartin Algesten } else { 303ffe74184SMartin Algesten let data = got.ok().unwrap(); 304ffe74184SMartin Algesten let buf = &mut data.clone(); 3055d8fe953SJoão Oliveira let actual = Header::unmarshal(buf).unwrap_or_else(|_| panic!("Unmarshal {name}")); 306ffe74184SMartin Algesten 307ffe74184SMartin Algesten assert_eq!( 308ffe74184SMartin Algesten actual, want, 3095d8fe953SJoão Oliveira "{name} round trip: got {actual:?}, want {want:?}" 310ffe74184SMartin Algesten ) 311ffe74184SMartin Algesten } 312ffe74184SMartin Algesten } 313ffe74184SMartin Algesten } 314ffe74184SMartin Algesten } 315