rtp_parse/rtcp/
rtcp_bye.rs

1use anyhow::Context;
2use parsely_rs::*;
3
4use super::rtcp_header::RtcpHeader;
5
6#[derive(Debug, PartialEq)]
7pub struct RtcpByeReason(String);
8
9impl RtcpByeReason {
10    pub fn new(reason: &str) -> Self {
11        Self(reason.to_owned())
12    }
13
14    pub fn length_bytes(&self) -> usize {
15        self.0.len()
16    }
17}
18
19impl PartialEq<String> for RtcpByeReason {
20    fn eq(&self, other: &String) -> bool {
21        &self.0 == other
22    }
23}
24
25impl PartialEq<&str> for RtcpByeReason {
26    fn eq(&self, other: &&str) -> bool {
27        &self.0 == other
28    }
29}
30
31impl<B: BitBuf> ParselyRead<B> for RtcpByeReason {
32    type Ctx = ();
33    fn read<T: ByteOrder>(buf: &mut B, _ctx: Self::Ctx) -> ParselyResult<Self> {
34        let length_bytes = buf.get_u8().context("Reading reason length bytes")?;
35        let mut data = vec![0; length_bytes as usize];
36        buf.try_copy_to_slice_bytes(&mut data[..])
37            .context("Reading reason data")?;
38        let reason_str = String::from_utf8(data).context("Converting reason data to string")?;
39        Ok(RtcpByeReason(reason_str))
40    }
41}
42
43impl_stateless_sync!(RtcpByeReason);
44
45impl<B: BitBufMut> ParselyWrite<B> for RtcpByeReason {
46    type Ctx = ();
47    fn write<T: ByteOrder>(&self, buf: &mut B, _ctx: Self::Ctx) -> ParselyResult<()> {
48        buf.put_u8(self.length_bytes() as u8)
49            .context("Writing reason string length")?;
50        buf.try_put_slice_bytes(self.0.as_bytes())
51            .context("Writing reason string")?;
52
53        Ok(())
54    }
55}
56
57/// https://datatracker.ietf.org/doc/html/rfc3550#section-6.6
58///        0                   1                   2                   3
59///        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
60///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
61///       |V=2|P|    SC   |   PT=BYE=203  |             length            |
62///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63///       |                           SSRC/CSRC                           |
64///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65///       :                              ...                              :
66///       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
67/// (opt) |     length    |               reason for leaving            ...
68///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
69#[derive(Debug, PartialEq, ParselyRead, ParselyWrite)]
70#[parsely_read(required_context("rtcp_header: RtcpHeader"))]
71#[parsely(alignment = 4)]
72pub struct RtcpByePacket {
73    #[parsely_read(assign_from = "rtcp_header")]
74    #[parsely_write(sync_with("self.payload_length_bytes()", "self.ssrcs.len().try_into()"))]
75    pub header: RtcpHeader,
76    #[parsely_read(count = "header.report_count.into()")]
77    pub ssrcs: Vec<u32>,
78    #[parsely_read(when = "buf.remaining_bytes() > 0")]
79    pub reason: Option<RtcpByeReason>,
80}
81
82impl Default for RtcpByePacket {
83    fn default() -> Self {
84        let header = RtcpHeader {
85            packet_type: RtcpByePacket::PT,
86            ..Default::default()
87        };
88        Self {
89            header,
90            ssrcs: Default::default(),
91            reason: Default::default(),
92        }
93    }
94}
95
96impl RtcpByePacket {
97    pub const PT: u8 = 203;
98
99    pub fn payload_length_bytes(&self) -> u16 {
100        // The payload's length in bytes is the number of ssrcs * 4 plus the reason length and the
101        // leading byte to describe its length (if there is a reason)
102        let mut payload_length_bytes =
103            self.ssrcs.len() * 4 + self.reason.as_ref().map_or(0, |r| r.length_bytes() + 1);
104
105        while payload_length_bytes % 4 != 0 {
106            payload_length_bytes += 1
107        }
108        payload_length_bytes as u16
109    }
110
111    pub fn add_ssrc(mut self, ssrc: u32) -> Self {
112        self.ssrcs.push(ssrc);
113        self
114    }
115
116    pub fn with_reason(mut self, reason: &str) -> Self {
117        self.reason = Some(RtcpByeReason::new(reason));
118        self
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    const TEST_RTCP_HEADER: RtcpHeader = RtcpHeader {
127        version: u2::new(2),
128        has_padding: false,
129        report_count: u5::new(2),
130        packet_type: 203,
131        length_field: 2,
132    };
133
134    #[test]
135    fn test_read_success() {
136        let reason_str = "goodbye";
137        let reason_bytes = reason_str.bytes();
138        #[rustfmt::skip]
139        let mut payload = vec![
140            // ssrc 1
141            0x00, 0x00, 0x00, 0x01, 
142            // ssrc 2
143            0x00, 0x00, 0x00, 0x02,
144            // reason length
145            reason_bytes.len() as u8
146        ];
147        // add reason bytes
148        payload.extend(reason_bytes.collect::<Vec<u8>>());
149        let mut bits = Bits::from_owner_bytes(payload);
150        let rtcp_bye = RtcpByePacket::read::<NetworkOrder>(&mut bits, (TEST_RTCP_HEADER,)).unwrap();
151        assert!(rtcp_bye.ssrcs.contains(&1u32));
152        assert!(rtcp_bye.ssrcs.contains(&2u32));
153        assert_eq!(rtcp_bye.reason.unwrap(), reason_str);
154    }
155
156    #[test]
157    fn test_read_success_no_reason() {
158        #[rustfmt::skip]
159        let payload = vec![
160            // ssrc 1
161            0x00, 0x00, 0x00, 0x01, 
162            // ssrc 2
163            0x00, 0x00, 0x00, 0x02,
164        ];
165        let mut bits = Bits::from_owner_bytes(payload);
166        let rtcp_bye = RtcpByePacket::read::<NetworkOrder>(&mut bits, (TEST_RTCP_HEADER,)).unwrap();
167        assert!(rtcp_bye.ssrcs.contains(&1u32));
168        assert!(rtcp_bye.ssrcs.contains(&2u32));
169        assert!(rtcp_bye.reason.is_none());
170    }
171
172    #[test]
173    fn test_read_missing_ssrc() {
174        // Report count (source count) is 2 in header, but we'll just have 1 SSRC in the payload
175        let mut bits = Bits::from_static_bytes(&[1, 2, 3, 4]);
176        let result = RtcpByePacket::read::<NetworkOrder>(&mut bits, (TEST_RTCP_HEADER,));
177        assert!(result.is_err());
178    }
179
180    #[test]
181    fn test_read_bad_utf8_reason() {
182        #[rustfmt::skip]
183        let payload = vec![
184            // ssrc 1
185            0x00, 0x00, 0x00, 0x01,
186            // ssrc 2
187            0x00, 0x00, 0x00, 0x02,
188            // length 2, invalid utf 8
189            0x02, 0xFF, 0xFF
190        ];
191        let mut bits = Bits::from_owner_bytes(payload);
192        let result = RtcpByePacket::read::<NetworkOrder>(&mut bits, (TEST_RTCP_HEADER,));
193        assert!(result.is_err());
194    }
195
196    #[test]
197    fn test_read_consume_padding() {
198        let reason_str = "g";
199        let reason_bytes = reason_str.bytes();
200        #[rustfmt::skip]
201        let mut payload = vec![
202            // ssrc 1
203            0x00, 0x00, 0x00, 0x01, 
204            // ssrc 2
205            0x00, 0x00, 0x00, 0x02,
206            // reason length
207            reason_bytes.len() as u8
208        ];
209        // add reason bytes
210        payload.extend(reason_bytes.collect::<Vec<u8>>());
211        // 2 bytes of padding
212        payload.extend([0x00, 0x00]);
213        let mut buf = Bits::from_owner_bytes(payload);
214        let _rtcp_bye = RtcpByePacket::read::<NetworkOrder>(&mut buf, (TEST_RTCP_HEADER,))
215            .expect("Successful read");
216        // Make sure the buffer was fully consumed
217        assert_eq!(buf.remaining_bytes(), 0);
218    }
219
220    #[test]
221    fn test_sync() {
222        let mut rtcp_bye = RtcpByePacket::default().add_ssrc(42);
223        rtcp_bye.sync(()).unwrap();
224        assert_eq!(rtcp_bye.header.packet_type, RtcpByePacket::PT);
225        assert_eq!(rtcp_bye.header.report_count, 1);
226        assert_eq!(rtcp_bye.header.length_field, 1);
227
228        let mut rtcp_bye = rtcp_bye.with_reason("goodbye");
229        rtcp_bye.sync(()).unwrap();
230        assert_eq!(rtcp_bye.header.length_field, 3);
231    }
232
233    #[test]
234    fn test_write_success() {
235        let mut rtcp_bye = RtcpByePacket {
236            header: TEST_RTCP_HEADER,
237            ssrcs: vec![42],
238            reason: None,
239        };
240        rtcp_bye.sync(()).unwrap();
241        let syncd_rtcp_header = rtcp_bye.header.clone();
242        let mut buf_mut = BitsMut::new();
243
244        rtcp_bye
245            .write::<NetworkOrder>(&mut buf_mut, ())
246            .expect("successful write");
247
248        // Now read from the buffer and compare
249        let mut buf = buf_mut.freeze();
250        let read_rtcp_header =
251            RtcpHeader::read::<NetworkOrder>(&mut buf, ()).expect("successul read");
252        assert_eq!(read_rtcp_header, syncd_rtcp_header);
253        let payload_length_bytes = read_rtcp_header.payload_length_bytes().unwrap();
254        let mut bye_subbuf = (&mut buf).take_bytes(payload_length_bytes as usize);
255        let read_rtcp_bye =
256            RtcpByePacket::read::<NetworkOrder>(&mut bye_subbuf, (read_rtcp_header,))
257                .expect("successful read");
258
259        assert_eq!(rtcp_bye, read_rtcp_bye);
260    }
261
262    #[test]
263    fn test_write_success_with_reason() {
264        let mut rtcp_bye = RtcpByePacket {
265            header: TEST_RTCP_HEADER,
266            ssrcs: vec![42],
267            reason: Some(RtcpByeReason::new("Goodbye")),
268        };
269        rtcp_bye.sync(()).unwrap();
270        let syncd_rtcp_header = rtcp_bye.header.clone();
271        let mut buf_mut = BitsMut::new();
272
273        rtcp_bye
274            .write::<NetworkOrder>(&mut buf_mut, ())
275            .expect("successful write");
276
277        // Now read from the buffer and compare
278        let mut buf = buf_mut.freeze();
279        let read_rtcp_header =
280            RtcpHeader::read::<NetworkOrder>(&mut buf, ()).expect("successul read");
281        assert_eq!(read_rtcp_header, syncd_rtcp_header);
282        let payload_length_bytes = read_rtcp_header.payload_length_bytes().unwrap();
283        let mut bye_subbuf = (&mut buf).take_bytes(payload_length_bytes as usize);
284        let read_rtcp_bye =
285            RtcpByePacket::read::<NetworkOrder>(&mut bye_subbuf, (read_rtcp_header,))
286                .expect("successful read");
287
288        assert_eq!(rtcp_bye, read_rtcp_bye);
289    }
290
291    #[test]
292    fn test_write_success_with_padding() {
293        let mut rtcp_bye = RtcpByePacket {
294            header: TEST_RTCP_HEADER,
295            ssrcs: vec![42],
296            reason: Some(RtcpByeReason::new("G")),
297        };
298        rtcp_bye.sync(()).unwrap();
299        let mut buf_mut = BitsMut::new();
300
301        rtcp_bye
302            .write::<NetworkOrder>(&mut buf_mut, ())
303            .expect("successful write");
304
305        // Make sure we landed on a word boundary
306        assert!(buf_mut.len() % 32 == 0);
307    }
308}