#[derive(Debug, Clone)]
pub struct RtpPacket<'a> {
pub version: u8,
pub padding: bool,
pub extension: bool,
pub marker: bool,
pub payload_type: u8,
pub sequence_number: u16,
pub timestamp: u32,
pub ssrc: u32,
pub csrc: Vec<u32>,
pub ext_profile: u16,
pub ext_data: &'a [u8],
pub payload: &'a [u8],
}
impl<'a> RtpPacket<'a> {
pub fn parse(buf: &'a [u8]) -> Option<Self> {
if buf.len() < 12 {
return None;
}
let b0 = buf[0];
let version = b0 >> 6;
if version != 2 {
return None;
}
let padding = b0 & 0x20 != 0;
let extension = b0 & 0x10 != 0;
let csrc_count = (b0 & 0x0f) as usize;
let b1 = buf[1];
let marker = b1 & 0x80 != 0;
let payload_type = b1 & 0x7f;
let sequence_number = u16::from_be_bytes([buf[2], buf[3]]);
let timestamp = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
let ssrc = u32::from_be_bytes([buf[8], buf[9], buf[10], buf[11]]);
let mut off = 12;
let mut csrc = Vec::with_capacity(csrc_count);
for _ in 0..csrc_count {
let end = off + 4;
let w = buf.get(off..end)?;
csrc.push(u32::from_be_bytes([w[0], w[1], w[2], w[3]]));
off = end;
}
let mut ext_profile = 0u16;
let mut ext_data: &[u8] = &[];
if extension {
let hdr = buf.get(off..off + 4)?;
ext_profile = u16::from_be_bytes([hdr[0], hdr[1]]);
let ext_words = u16::from_be_bytes([hdr[2], hdr[3]]) as usize;
off += 4;
let ext_len = ext_words * 4;
ext_data = buf.get(off..off + ext_len)?;
off += ext_len;
}
let mut end = buf.len();
if padding {
let pad = *buf.last()? as usize;
if pad == 0 || pad > end.saturating_sub(off) {
return None;
}
end -= pad;
}
let payload = buf.get(off..end)?;
Some(RtpPacket {
version,
padding,
extension,
marker,
payload_type,
sequence_number,
timestamp,
ssrc,
csrc,
ext_profile,
ext_data,
payload,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_basic_rtp() {
let mut pkt = vec![0x80, 0x80 | 100, 0x00, 0x01, 0, 0, 0, 2, 0, 0, 0, 3];
pkt.extend_from_slice(b"hi");
let p = RtpPacket::parse(&pkt).unwrap();
assert_eq!(p.payload_type, 100);
assert!(p.marker);
assert_eq!(p.sequence_number, 1);
assert_eq!(p.timestamp, 2);
assert_eq!(p.ssrc, 3);
assert_eq!(p.payload, b"hi");
}
#[test]
fn parses_extension_header() {
let pkt = vec![
0x90, 100, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, 0xBE, 0xDE, 0, 1, 0xAA, 0xBB, 0xCC, 0xDD, 0x01, 0x02, ];
let p = RtpPacket::parse(&pkt).unwrap();
assert_eq!(p.ext_profile, 0xBEDE);
assert_eq!(p.ext_data, &[0xAA, 0xBB, 0xCC, 0xDD]);
assert_eq!(p.payload, &[0x01, 0x02]);
}
}