arcly-stream 0.1.6

An open-extensible live-media streaming kernel: lock-free zero-copy frame fan-out, instant-start GOP cache, a pluggable multi-protocol ingestion layer (RTMP, RTSP, SRT, WHIP/WHEP shipped), and a feature-gated pure-Rust media plane (MPEG-TS/HLS/fMP4) — runtime, config, and metrics free.
Documentation
//! SRT packet header parsing (draft-sharabayko-srt §3).
//!
//! Every SRT packet opens with a 16-byte header. The high bit of the first word
//! selects the packet kind: `0` = data, `1` = control.

/// SRT control packet types (the 15-bit type field of a control header).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ControlType {
    /// `HANDSHAKE` (0x0000) — connection setup (induction/conclusion).
    Handshake,
    /// `KEEPALIVE` (0x0001).
    Keepalive,
    /// `ACK` (0x0002).
    Ack,
    /// `NAK` / loss report (0x0003).
    Nak,
    /// `SHUTDOWN` (0x0005) — peer is closing.
    Shutdown,
    /// `ACKACK` (0x0006).
    AckAck,
    /// Any other / vendor control type, carrying its raw type value.
    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),
        }
    }
}

/// A parsed SRT packet — its header fields and, for data packets, the payload
/// offset (always 16, the fixed header length).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SrtPacket {
    /// A control packet.
    Control {
        /// The control packet type.
        control_type: ControlType,
        /// Destination SRT socket id (last header word).
        dest_socket_id: u32,
    },
    /// A data packet carrying media (here: MPEG-TS) bytes.
    Data {
        /// 31-bit packet sequence number.
        sequence: u32,
        /// Destination SRT socket id.
        dest_socket_id: u32,
        /// Offset at which the payload begins (16).
        payload_offset: usize,
    },
}

impl SrtPacket {
    /// Parse the 16-byte SRT header from `buf`. Returns `None` if `buf` is
    /// shorter than a header.
    pub fn parse(buf: &[u8]) -> Option<SrtPacket> {
        use crate::protocol::byteops::ByteReader;
        let mut r = ByteReader::new(buf);
        let word0 = r.u32_be()?;
        r.skip(8)?; // timestamp + message-number fields
        let dest_socket_id = r.u32_be()?;

        if word0 & 0x8000_0000 == 0 {
            // Data packet: bit 0 is the control flag (0), bits 1..31 the seq num.
            Some(SrtPacket::Data {
                sequence: word0 & 0x7FFF_FFFF,
                dest_socket_id,
                payload_offset: 16,
            })
        } else {
            // Control packet: bits 1..15 = type, bits 16..31 = subtype.
            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]); // ts + msg fields
        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() {
        // Control flag set, type 0x0000 (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());
    }
}