#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ControlType {
Handshake,
Keepalive,
Ack,
Nak,
Shutdown,
AckAck,
Other(u16),
}
impl ControlType {
fn from_u16(v: u16) -> ControlType {
match v {
0x0000 => ControlType::Handshake,
0x0001 => ControlType::Keepalive,
0x0002 => ControlType::Ack,
0x0003 => ControlType::Nak,
0x0005 => ControlType::Shutdown,
0x0006 => ControlType::AckAck,
other => ControlType::Other(other),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SrtPacket {
Control {
control_type: ControlType,
dest_socket_id: u32,
},
Data {
sequence: u32,
dest_socket_id: u32,
payload_offset: usize,
},
}
impl SrtPacket {
pub fn parse(buf: &[u8]) -> Option<SrtPacket> {
if buf.len() < 16 {
return None;
}
let word0 = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]);
let dest_socket_id = u32::from_be_bytes([buf[12], buf[13], buf[14], buf[15]]);
if word0 & 0x8000_0000 == 0 {
Some(SrtPacket::Data {
sequence: word0 & 0x7FFF_FFFF,
dest_socket_id,
payload_offset: 16,
})
} else {
let control_type = ((word0 >> 16) & 0x7FFF) as u16;
Some(SrtPacket::Control {
control_type: ControlType::from_u16(control_type),
dest_socket_id,
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn header(word0: u32, dest: u32) -> Vec<u8> {
let mut b = word0.to_be_bytes().to_vec();
b.extend_from_slice(&[0; 8]); b.extend_from_slice(&dest.to_be_bytes());
b
}
#[test]
fn parses_data_packet() {
let pkt = SrtPacket::parse(&header(0x0000_002A, 99)).unwrap();
assert_eq!(
pkt,
SrtPacket::Data {
sequence: 42,
dest_socket_id: 99,
payload_offset: 16,
}
);
}
#[test]
fn parses_control_handshake() {
let pkt = SrtPacket::parse(&header(0x8000_0000, 7)).unwrap();
assert_eq!(
pkt,
SrtPacket::Control {
control_type: ControlType::Handshake,
dest_socket_id: 7,
}
);
}
#[test]
fn parses_control_nak() {
let pkt = SrtPacket::parse(&header(0x8003_0000, 1)).unwrap();
match pkt {
SrtPacket::Control { control_type, .. } => assert_eq!(control_type, ControlType::Nak),
_ => panic!("expected control"),
}
}
#[test]
fn rejects_short_buffer() {
assert!(SrtPacket::parse(&[0u8; 8]).is_none());
}
}