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#[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 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 0x00, 0x00, 0x00, 0x01,
142 0x00, 0x00, 0x00, 0x02,
144 reason_bytes.len() as u8
146 ];
147 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 0x00, 0x00, 0x00, 0x01,
162 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 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 0x00, 0x00, 0x00, 0x01,
186 0x00, 0x00, 0x00, 0x02,
188 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 0x00, 0x00, 0x00, 0x01,
204 0x00, 0x00, 0x00, 0x02,
206 reason_bytes.len() as u8
208 ];
209 payload.extend(reason_bytes.collect::<Vec<u8>>());
211 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 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 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 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 assert!(buf_mut.len() % 32 == 0);
307 }
308}