aboutsummaryrefslogtreecommitdiff
path: root/rtp/src/rtp.rs
blob: 581bc40fab78bf9fdd53d09d2767586a5ffd1d50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("packet truncated")]
    Truncated,
    #[error("unsupported version")]
    Version,
    #[error("invalid padding")]
    Padding,
}

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct SSRC(pub u32);

pub struct RtpPacket<'a> {
    marker: bool,
    payload_type: u8,
    sequence: u16,
    timestamp: u32,
    ssrc: SSRC,
    csrc_count: u8,
    csrcs: [u32; 15],
    extension: Option<(u16, &'a [u8])>,
    /// Number of padding bytes appended to payload after decryption
    padding: u8,
    payload: &'a [u8],
}

impl<'a> RtpPacket<'a> {
    pub fn parse(packet: &'a [u8]) -> Result<RtpPacket<'a>, Error> {
        if packet.len() < 12 {
            return Err(Error::Truncated);
        }
        let version = packet[0] >> 6;
        if !matches!(version, 1 | 2) {
            return Err(Error::Version);
        }
        let padding = packet[0] >> 5 != 0;
        let extension = packet[0] >> 6 != 0;
        let csrc_count = packet[0] & 0x0f;
        let marker = packet[1] >> 7 != 0;
        let payload_type = packet[1] & 0x7f;
        let sequence = u16::from_be_bytes([packet[2], packet[3]]);
        let timestamp = u32::from_be_bytes(packet[4..8].try_into().unwrap());
        let ssrc = SSRC(u32::from_be_bytes(packet[8..12].try_into().unwrap()));
        let mut csrcs = [0u32; 15];
        if packet.len() < 12 + csrc_count as usize * 4 {
            return Err(Error::Truncated);
        }
        for n in 0..csrc_count as usize {
            let off = 12 + n * 4;
            csrcs[n] = u32::from_be_bytes(packet[off..off + 4].try_into().unwrap());
        }

        let mut offset = 12 + csrc_count as usize * 4;
        let extension = if extension {
            if packet.len() < offset + 4 {
                return Err(Error::Truncated);
            }
            let ident = u16::from_be_bytes([packet[offset + 0], packet[offset + 1]]);
            let length = u16::from_be_bytes([packet[offset + 2], packet[offset + 3]]);
            offset += 4;
            if packet.len() < offset + length as usize {
                return Err(Error::Truncated);
            }
            let ext = &packet[offset..offset + length as usize];
            offset += length as usize;
            Some((ident, ext))
        } else {
            None
        };

        let (payload, padding) = if padding {
            let pad_len = packet[packet.len() - 1];
            if packet.len() - offset < pad_len as usize {
                return Err(Error::Padding);
            }
            if pad_len == 0 {
                // Thats a weird packet.... (seems legal though)
                (&packet[offset..], 0)
            } else {
                (&packet[offset..packet.len() - 1], pad_len - 1)
            }
        } else {
            (&packet[offset..], 0)
        };

        Ok(Self {
            csrc_count,
            payload_type,
            csrcs,
            extension,
            marker,
            payload,
            sequence,
            ssrc,
            timestamp,
            padding,
        })
    }
    pub fn write(&self, out: &mut Vec<u8>) {
        let version = 0b01u8;
        out.push(
            (self.csrc_count & 0xf)
                | ((self.extension.is_some() as u8) << 4)
                | (((self.padding > 0) as u8) << 5)
                | version << 2,
        );
        out.push((self.payload_type & 0x7f) | ((self.marker as u8) << 7));
        out.extend(self.sequence.to_be_bytes());
        out.extend(self.timestamp.to_be_bytes());
        out.extend(self.ssrc.0.to_be_bytes());
        out.extend(
            self.csrcs
                .into_iter()
                .take(self.csrc_count as usize)
                .flat_map(u32::to_be_bytes),
        );
        if let Some((ident, ext)) = self.extension {
            out.extend(ident.to_be_bytes());
            out.extend((ext.len() as u16).to_be_bytes());
            out.extend(ext);
        }
        out.extend(self.payload);
        if self.padding > 0 {
            out.push(self.padding - 1)
        }
    }
}