aboutsummaryrefslogtreecommitdiff
path: root/rtp/src/rtp.rs
blob: a1ed166ad8744b97749fa99091c54c46c6e72afd (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
129
130
#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("packet truncated")]
    Truncated,
    #[error("unsupported version")]
    Version,
    #[error("invalid padding")]
    Padding,
}

pub struct RtpPacket<'a> {
    marker: bool,
    payload_type: u8,
    sequence: u16,
    timestamp: u32,
    ssrc: u32,
    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], packet[5], packet[6], packet[7]]);
        let ssrc = u32::from_be_bytes([packet[8], packet[9], packet[10], packet[11]]);
        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 + 0],
                packet[off + 1],
                packet[off + 2],
                packet[off + 3],
            ]);
        }

        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.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)
        }
    }
}