Skip to main content

arcly_stream/protocol/srt/
packet.rs

1//! SRT packet header parsing (draft-sharabayko-srt §3).
2//!
3//! Every SRT packet opens with a 16-byte header. The high bit of the first word
4//! selects the packet kind: `0` = data, `1` = control.
5
6/// SRT control packet types (the 15-bit type field of a control header).
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum ControlType {
10    /// `HANDSHAKE` (0x0000) — connection setup (induction/conclusion).
11    Handshake,
12    /// `KEEPALIVE` (0x0001).
13    Keepalive,
14    /// `ACK` (0x0002).
15    Ack,
16    /// `NAK` / loss report (0x0003).
17    Nak,
18    /// `SHUTDOWN` (0x0005) — peer is closing.
19    Shutdown,
20    /// `ACKACK` (0x0006).
21    AckAck,
22    /// Any other / vendor control type, carrying its raw type value.
23    Other(u16),
24}
25
26impl ControlType {
27    fn from_u16(v: u16) -> ControlType {
28        match v {
29            0x0000 => ControlType::Handshake,
30            0x0001 => ControlType::Keepalive,
31            0x0002 => ControlType::Ack,
32            0x0003 => ControlType::Nak,
33            0x0005 => ControlType::Shutdown,
34            0x0006 => ControlType::AckAck,
35            other => ControlType::Other(other),
36        }
37    }
38}
39
40/// A parsed SRT packet — its header fields and, for data packets, the payload
41/// offset (always 16, the fixed header length).
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum SrtPacket {
44    /// A control packet.
45    Control {
46        /// The control packet type.
47        control_type: ControlType,
48        /// Destination SRT socket id (last header word).
49        dest_socket_id: u32,
50    },
51    /// A data packet carrying media (here: MPEG-TS) bytes.
52    Data {
53        /// 31-bit packet sequence number.
54        sequence: u32,
55        /// Destination SRT socket id.
56        dest_socket_id: u32,
57        /// Offset at which the payload begins (16).
58        payload_offset: usize,
59    },
60}
61
62impl SrtPacket {
63    /// Parse the 16-byte SRT header from `buf`. Returns `None` if `buf` is
64    /// shorter than a header.
65    pub fn parse(buf: &[u8]) -> Option<SrtPacket> {
66        use crate::protocol::byteops::ByteReader;
67        let mut r = ByteReader::new(buf);
68        let word0 = r.u32_be()?;
69        r.skip(8)?; // timestamp + message-number fields
70        let dest_socket_id = r.u32_be()?;
71
72        if word0 & 0x8000_0000 == 0 {
73            // Data packet: bit 0 is the control flag (0), bits 1..31 the seq num.
74            Some(SrtPacket::Data {
75                sequence: word0 & 0x7FFF_FFFF,
76                dest_socket_id,
77                payload_offset: 16,
78            })
79        } else {
80            // Control packet: bits 1..15 = type, bits 16..31 = subtype.
81            let control_type = ((word0 >> 16) & 0x7FFF) as u16;
82            Some(SrtPacket::Control {
83                control_type: ControlType::from_u16(control_type),
84                dest_socket_id,
85            })
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    fn header(word0: u32, dest: u32) -> Vec<u8> {
95        let mut b = word0.to_be_bytes().to_vec();
96        b.extend_from_slice(&[0; 8]); // ts + msg fields
97        b.extend_from_slice(&dest.to_be_bytes());
98        b
99    }
100
101    #[test]
102    fn parses_data_packet() {
103        let pkt = SrtPacket::parse(&header(0x0000_002A, 99)).unwrap();
104        assert_eq!(
105            pkt,
106            SrtPacket::Data {
107                sequence: 42,
108                dest_socket_id: 99,
109                payload_offset: 16,
110            }
111        );
112    }
113
114    #[test]
115    fn parses_control_handshake() {
116        // Control flag set, type 0x0000 (handshake).
117        let pkt = SrtPacket::parse(&header(0x8000_0000, 7)).unwrap();
118        assert_eq!(
119            pkt,
120            SrtPacket::Control {
121                control_type: ControlType::Handshake,
122                dest_socket_id: 7,
123            }
124        );
125    }
126
127    #[test]
128    fn parses_control_nak() {
129        let pkt = SrtPacket::parse(&header(0x8003_0000, 1)).unwrap();
130        match pkt {
131            SrtPacket::Control { control_type, .. } => assert_eq!(control_type, ControlType::Nak),
132            _ => panic!("expected control"),
133        }
134    }
135
136    #[test]
137    fn rejects_short_buffer() {
138        assert!(SrtPacket::parse(&[0u8; 8]).is_none());
139    }
140}