Skip to main content

rtp_core/
packet.rs

1use thiserror::Error;
2
3/// RTP packet (RFC 3550)
4///
5/// ```text
6///  0                   1                   2                   3
7///  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
8/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
9/// |V=2|P|X|  CC   |M|     PT      |       sequence number         |
10/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11/// |                           timestamp                           |
12/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13/// |           synchronization source (SSRC) identifier            |
14/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15/// ```
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct RtpPacket {
18    pub version: u8,
19    pub padding: bool,
20    pub extension: bool,
21    pub csrc_count: u8,
22    pub marker: bool,
23    pub payload_type: u8,
24    pub sequence_number: u16,
25    pub timestamp: u32,
26    pub ssrc: u32,
27    pub csrc: Vec<u32>,
28    pub payload: Vec<u8>,
29}
30
31#[derive(Debug, Error)]
32pub enum RtpError {
33    #[error("packet too short: {0} bytes")]
34    TooShort(usize),
35    #[error("invalid RTP version: {0}")]
36    InvalidVersion(u8),
37    #[error("buffer too small")]
38    BufferTooSmall,
39}
40
41impl RtpPacket {
42    /// Minimum RTP header size (no CSRC, no extension)
43    pub const MIN_HEADER_SIZE: usize = 12;
44
45    /// Create a new RTP packet
46    pub fn new(payload_type: u8, sequence_number: u16, timestamp: u32, ssrc: u32) -> Self {
47        Self {
48            version: 2,
49            padding: false,
50            extension: false,
51            csrc_count: 0,
52            marker: false,
53            payload_type,
54            sequence_number,
55            timestamp,
56            ssrc,
57            csrc: Vec::new(),
58            payload: Vec::new(),
59        }
60    }
61
62    /// Set the payload data
63    pub fn with_payload(mut self, payload: Vec<u8>) -> Self {
64        self.payload = payload;
65        self
66    }
67
68    /// Set the marker bit
69    pub fn with_marker(mut self, marker: bool) -> Self {
70        self.marker = marker;
71        self
72    }
73
74    /// Parse an RTP packet from bytes
75    pub fn parse(data: &[u8]) -> Result<Self, RtpError> {
76        if data.len() < Self::MIN_HEADER_SIZE {
77            return Err(RtpError::TooShort(data.len()));
78        }
79
80        let version = (data[0] >> 6) & 0x03;
81        if version != 2 {
82            return Err(RtpError::InvalidVersion(version));
83        }
84
85        let padding = (data[0] >> 5) & 0x01 != 0;
86        let extension = (data[0] >> 4) & 0x01 != 0;
87        let csrc_count = data[0] & 0x0F;
88        let marker = (data[1] >> 7) & 0x01 != 0;
89        let payload_type = data[1] & 0x7F;
90        let sequence_number = u16::from_be_bytes([data[2], data[3]]);
91        let timestamp = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
92        let ssrc = u32::from_be_bytes([data[8], data[9], data[10], data[11]]);
93
94        let header_size = Self::MIN_HEADER_SIZE + (csrc_count as usize * 4);
95        if data.len() < header_size {
96            return Err(RtpError::TooShort(data.len()));
97        }
98
99        let mut csrc = Vec::with_capacity(csrc_count as usize);
100        for i in 0..csrc_count as usize {
101            let offset = Self::MIN_HEADER_SIZE + (i * 4);
102            let csrc_id = u32::from_be_bytes([
103                data[offset],
104                data[offset + 1],
105                data[offset + 2],
106                data[offset + 3],
107            ]);
108            csrc.push(csrc_id);
109        }
110
111        let mut payload_offset = header_size;
112
113        // Skip extension header if present
114        if extension && data.len() >= payload_offset + 4 {
115            let ext_length =
116                u16::from_be_bytes([data[payload_offset + 2], data[payload_offset + 3]]) as usize;
117            payload_offset += 4 + (ext_length * 4);
118        }
119
120        let payload_end = if padding && !data.is_empty() {
121            let padding_len = data[data.len() - 1] as usize;
122            data.len().saturating_sub(padding_len)
123        } else {
124            data.len()
125        };
126
127        let payload = if payload_offset <= payload_end {
128            data[payload_offset..payload_end].to_vec()
129        } else {
130            Vec::new()
131        };
132
133        Ok(RtpPacket {
134            version,
135            padding,
136            extension,
137            csrc_count,
138            marker,
139            payload_type,
140            sequence_number,
141            timestamp,
142            ssrc,
143            csrc,
144            payload,
145        })
146    }
147
148    /// Serialize the RTP packet to bytes
149    pub fn serialize(&self) -> Vec<u8> {
150        let header_size = Self::MIN_HEADER_SIZE + (self.csrc.len() * 4);
151        let mut buf = Vec::with_capacity(header_size + self.payload.len());
152
153        // Byte 0: V=2, P, X, CC
154        let byte0 = (self.version << 6)
155            | ((self.padding as u8) << 5)
156            | ((self.extension as u8) << 4)
157            | (self.csrc.len() as u8 & 0x0F);
158        buf.push(byte0);
159
160        // Byte 1: M, PT
161        let byte1 = ((self.marker as u8) << 7) | (self.payload_type & 0x7F);
162        buf.push(byte1);
163
164        // Bytes 2-3: sequence number
165        buf.extend_from_slice(&self.sequence_number.to_be_bytes());
166
167        // Bytes 4-7: timestamp
168        buf.extend_from_slice(&self.timestamp.to_be_bytes());
169
170        // Bytes 8-11: SSRC
171        buf.extend_from_slice(&self.ssrc.to_be_bytes());
172
173        // CSRC list
174        for csrc_id in &self.csrc {
175            buf.extend_from_slice(&csrc_id.to_be_bytes());
176        }
177
178        // Payload
179        buf.extend_from_slice(&self.payload);
180
181        buf
182    }
183
184    /// Get the total size of the serialized packet
185    pub fn size(&self) -> usize {
186        Self::MIN_HEADER_SIZE + (self.csrc.len() * 4) + self.payload.len()
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193
194    #[test]
195    fn test_new_packet() {
196        let pkt = RtpPacket::new(0, 1, 160, 0x12345678);
197        assert_eq!(pkt.version, 2);
198        assert_eq!(pkt.payload_type, 0);
199        assert_eq!(pkt.sequence_number, 1);
200        assert_eq!(pkt.timestamp, 160);
201        assert_eq!(pkt.ssrc, 0x12345678);
202        assert!(!pkt.marker);
203        assert!(pkt.payload.is_empty());
204    }
205
206    #[test]
207    fn test_with_payload() {
208        let pkt = RtpPacket::new(0, 1, 160, 0x12345678).with_payload(vec![1, 2, 3, 4]);
209        assert_eq!(pkt.payload, vec![1, 2, 3, 4]);
210    }
211
212    #[test]
213    fn test_with_marker() {
214        let pkt = RtpPacket::new(0, 1, 160, 0x12345678).with_marker(true);
215        assert!(pkt.marker);
216    }
217
218    #[test]
219    fn test_serialize_parse_roundtrip() {
220        let original = RtpPacket::new(0, 42, 3360, 0xDEADBEEF)
221            .with_payload(vec![0x80, 0xFF, 0x00, 0x7F, 0x01, 0x02])
222            .with_marker(true);
223
224        let bytes = original.serialize();
225        let parsed = RtpPacket::parse(&bytes).unwrap();
226
227        assert_eq!(parsed.version, 2);
228        assert_eq!(parsed.payload_type, 0);
229        assert_eq!(parsed.sequence_number, 42);
230        assert_eq!(parsed.timestamp, 3360);
231        assert_eq!(parsed.ssrc, 0xDEADBEEF);
232        assert!(parsed.marker);
233        assert_eq!(parsed.payload, vec![0x80, 0xFF, 0x00, 0x7F, 0x01, 0x02]);
234    }
235
236    #[test]
237    fn test_parse_pcmu_packet() {
238        // Construct a minimal PCMU RTP packet manually
239        let mut data = vec![
240            0x80, // V=2, P=0, X=0, CC=0
241            0x00, // M=0, PT=0 (PCMU)
242            0x00, 0x01, // seq=1
243            0x00, 0x00, 0x00, 0xA0, // timestamp=160
244            0x00, 0x00, 0x00, 0x01, // ssrc=1
245        ];
246        // Add 160 bytes of payload (one PCMU frame at 8kHz, 20ms)
247        data.extend_from_slice(&vec![0x7F; 160]);
248
249        let pkt = RtpPacket::parse(&data).unwrap();
250        assert_eq!(pkt.version, 2);
251        assert_eq!(pkt.payload_type, 0);
252        assert_eq!(pkt.sequence_number, 1);
253        assert_eq!(pkt.timestamp, 160);
254        assert_eq!(pkt.ssrc, 1);
255        assert_eq!(pkt.payload.len(), 160);
256    }
257
258    #[test]
259    fn test_parse_too_short() {
260        let data = vec![0x80, 0x00, 0x00];
261        let result = RtpPacket::parse(&data);
262        assert!(matches!(result, Err(RtpError::TooShort(3))));
263    }
264
265    #[test]
266    fn test_parse_invalid_version() {
267        let data = vec![
268            0x00, // V=0
269            0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x01,
270        ];
271        let result = RtpPacket::parse(&data);
272        assert!(matches!(result, Err(RtpError::InvalidVersion(0))));
273    }
274
275    #[test]
276    fn test_serialize_header_only() {
277        let pkt = RtpPacket::new(8, 100, 16000, 0xAAAABBBB);
278        let bytes = pkt.serialize();
279        assert_eq!(bytes.len(), RtpPacket::MIN_HEADER_SIZE);
280
281        // Check version bits
282        assert_eq!(bytes[0] & 0xC0, 0x80); // V=2
283
284        // Check PT
285        assert_eq!(bytes[1] & 0x7F, 8); // PT=8 (PCMA)
286
287        // Check seq
288        assert_eq!(u16::from_be_bytes([bytes[2], bytes[3]]), 100);
289    }
290
291    #[test]
292    fn test_multiple_roundtrips() {
293        for pt in [0u8, 8, 111] {
294            for seq in [0u16, 1, 65535] {
295                let pkt = RtpPacket::new(pt, seq, seq as u32 * 160, 0x11223344)
296                    .with_payload(vec![0xAA; 20]);
297                let bytes = pkt.serialize();
298                let parsed = RtpPacket::parse(&bytes).unwrap();
299                assert_eq!(parsed.payload_type, pt);
300                assert_eq!(parsed.sequence_number, seq);
301                assert_eq!(parsed.payload.len(), 20);
302            }
303        }
304    }
305
306    #[test]
307    fn test_packet_size() {
308        let pkt = RtpPacket::new(0, 1, 160, 1).with_payload(vec![0; 160]);
309        assert_eq!(pkt.size(), 12 + 160);
310
311        let pkt_empty = RtpPacket::new(0, 1, 160, 1);
312        assert_eq!(pkt_empty.size(), 12);
313    }
314
315    #[test]
316    fn test_csrc_roundtrip() {
317        let mut pkt = RtpPacket::new(0, 1, 160, 0x12345678);
318        pkt.csrc = vec![0xAAAA0001, 0xBBBB0002];
319        pkt.csrc_count = 2;
320        pkt.payload = vec![0xFF; 10];
321
322        let bytes = pkt.serialize();
323        let parsed = RtpPacket::parse(&bytes).unwrap();
324        assert_eq!(parsed.csrc_count, 2);
325        assert_eq!(parsed.csrc, vec![0xAAAA0001, 0xBBBB0002]);
326        assert_eq!(parsed.payload.len(), 10);
327    }
328
329    #[test]
330    fn test_marker_bit_serialization() {
331        let pkt = RtpPacket::new(111, 1, 960, 1).with_marker(true);
332        let bytes = pkt.serialize();
333        assert_eq!(bytes[1] & 0x80, 0x80); // Marker bit set
334
335        let pkt2 = RtpPacket::new(111, 1, 960, 1).with_marker(false);
336        let bytes2 = pkt2.serialize();
337        assert_eq!(bytes2[1] & 0x80, 0x00); // Marker bit clear
338    }
339}