1use crate::{
4 prelude::*,
5 utils::{parser, writer},
6 RtcpPacket, RtcpParseError, RtcpWriteError,
7};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct App<'a> {
12 data: &'a [u8],
13}
14
15impl RtcpPacket for App<'_> {
16 const MIN_PACKET_LEN: usize = 12;
17 const PACKET_TYPE: u8 = 204;
18}
19
20impl<'a> RtcpPacketParser<'a> for App<'a> {
21 fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
22 parser::check_packet::<Self>(data)?;
23
24 Ok(Self { data })
25 }
26
27 #[inline(always)]
28 fn header_data(&self) -> [u8; 4] {
29 self.data[..4].try_into().unwrap()
30 }
31}
32
33impl<'a> App<'a> {
34 const SUBTYPE_MASK: u8 = Self::MAX_COUNT;
35 pub const NAME_LEN: usize = 4;
37
38 pub fn padding(&self) -> Option<u8> {
40 parser::parse_padding(self.data)
41 }
42
43 pub fn ssrc(&self) -> u32 {
45 parser::parse_ssrc(self.data)
46 }
47
48 pub fn name(&self) -> [u8; App::NAME_LEN] {
51 self.data[8..8 + Self::NAME_LEN].try_into().unwrap()
52 }
53
54 pub fn get_name_string(&self) -> Result<String, std::string::FromUtf8Error> {
56 String::from_utf8(Vec::from_iter(self.name().iter().map_while(|&b| {
58 if b == 0 {
59 None
60 } else {
61 Some(b)
62 }
63 })))
64 }
65
66 pub fn data(&self) -> &[u8] {
68 &self.data[12..self.data.len() - self.padding().unwrap_or(0) as usize]
69 }
70
71 pub fn builder(ssrc: u32, name: &'a str) -> AppBuilder<'a> {
75 AppBuilder::new(ssrc, name)
76 }
77}
78
79#[derive(Debug)]
81#[must_use = "The builder must be built to be used"]
82pub struct AppBuilder<'a> {
83 ssrc: u32,
84 padding: u8,
85 subtype: u8,
86 name: &'a str,
87 data: &'a [u8],
88}
89
90impl<'a> AppBuilder<'a> {
91 fn new(ssrc: u32, name: &'a str) -> Self {
92 AppBuilder {
93 ssrc,
94 padding: 0,
95 subtype: 0,
96 name,
97 data: &[],
98 }
99 }
100
101 pub fn padding(mut self, padding: u8) -> Self {
103 self.padding = padding;
104 self
105 }
106
107 pub fn subtype(mut self, subtype: u8) -> Self {
109 self.subtype = subtype;
110 self
111 }
112
113 pub fn data(mut self, data: &'a [u8]) -> Self {
115 self.data = data;
116 self
117 }
118}
119
120impl RtcpPacketWriter for AppBuilder<'_> {
121 #[inline]
130 fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
131 if self.subtype > App::SUBTYPE_MASK {
132 return Err(RtcpWriteError::AppSubtypeOutOfRange {
133 subtype: self.subtype,
134 max: App::SUBTYPE_MASK,
135 });
136 }
137
138 if self.name.len() > App::NAME_LEN || !self.name.is_ascii() {
143 return Err(RtcpWriteError::InvalidName);
144 }
145
146 let mut size = App::MIN_PACKET_LEN + self.padding as usize;
147
148 if self.data.len() % 4 != 0 {
149 return Err(RtcpWriteError::DataLen32bitMultiple(self.data.len()));
150 }
151
152 size += self.data.len();
153
154 writer::check_padding(self.padding)?;
155
156 Ok(size)
157 }
158
159 #[inline]
167 fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
168 writer::write_header_unchecked::<App>(self.padding, self.subtype, buf);
169
170 buf[4..8].copy_from_slice(&self.ssrc.to_be_bytes());
171
172 let name = self.name.as_bytes();
173 let name_len = name.len();
174 let mut end = 8 + name_len;
175 buf[8..end].copy_from_slice(name);
176 if end < 12 {
178 buf[end..12].fill(0);
179 }
180
181 end = 12 + self.data.len();
182 buf[12..end].copy_from_slice(self.data);
183
184 end += writer::write_padding_unchecked(self.padding, &mut buf[end..]);
185
186 end
187 }
188
189 fn get_padding(&self) -> Option<u8> {
190 if self.padding == 0 {
191 return None;
192 }
193
194 Some(self.padding)
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn parse_empty_app() {
204 let data = [
205 0x80, 0xcc, 0x00, 0x02, 0x91, 0x82, 0x73, 0x64, 0x00, 0x00, 0x00, 0x00,
206 ];
207 let app = App::parse(&data).unwrap();
208 assert_eq!(app.version(), 2);
209 assert_eq!(app.padding(), None);
210 assert_eq!(app.subtype(), 0);
211 assert_eq!(app.name(), [0, 0, 0, 0]);
212 assert!(app.get_name_string().unwrap().is_empty());
213 assert!(app.data().is_empty());
214 }
215
216 #[test]
217 fn build_empty_app() {
218 const REQ_LEN: usize = App::MIN_PACKET_LEN;
219 let appb = App::builder(0x91827364, "name");
220 let req_len = appb.calculate_size().unwrap();
221 assert_eq!(req_len, REQ_LEN);
222
223 let mut data = [0; REQ_LEN];
224 let len = appb.write_into(&mut data).unwrap();
225
226 assert_eq!(len, REQ_LEN);
227 assert_eq!(
228 data,
229 [0x80, 0xcc, 0x00, 0x02, 0x91, 0x82, 0x73, 0x64, 0x6e, 0x61, 0x6d, 0x65,]
230 );
231 }
232
233 #[test]
234 fn parse_app() {
235 let data = [
236 0xbf, 0xcc, 0x00, 0x04, 0x91, 0x82, 0x73, 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x02,
237 0x03, 0x00, 0x00, 0x00, 0x00, 0x04,
238 ];
239 let app = App::parse(&data).unwrap();
240 assert_eq!(app.version(), 2);
241 assert_eq!(app.padding(), Some(4));
242 assert_eq!(app.subtype(), 31);
243 assert_eq!(app.name(), "name".as_bytes());
244 assert_eq!(app.get_name_string().unwrap(), "name");
245 assert_eq!(app.data(), [0x01, 0x02, 0x3, 0x0]);
246 }
247
248 #[test]
249 fn build_app() {
250 const REQ_LEN: usize = App::MIN_PACKET_LEN + 4 + 4;
251 let appb = App::builder(0x91827364, "name")
252 .padding(4)
253 .subtype(31)
254 .data(&[0x01, 0x02, 0x3, 0x0]);
255 let req_len = appb.calculate_size().unwrap();
256 assert_eq!(req_len, REQ_LEN);
257
258 let mut data = [0; REQ_LEN];
259 let len = appb.write_into(&mut data).unwrap();
260 assert_eq!(len, REQ_LEN);
261 assert_eq!(
262 data,
263 [
264 0xbf, 0xcc, 0x00, 0x04, 0x91, 0x82, 0x73, 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x02,
265 0x03, 0x00, 0x00, 0x00, 0x00, 0x04,
266 ]
267 );
268 }
269
270 #[test]
271 fn build_short_name() {
272 const REQ_LEN: usize = App::MIN_PACKET_LEN;
273 let appb = App::builder(0x91827364, "nam").subtype(31);
274 let req_len = appb.calculate_size().unwrap();
275 assert_eq!(req_len, REQ_LEN);
276
277 let mut data = [0; REQ_LEN];
278 let len = appb.write_into(&mut data).unwrap();
279 assert_eq!(len, REQ_LEN);
280 assert_eq!(
281 data,
282 [0x9f, 0xcc, 0x00, 0x02, 0x91, 0x82, 0x73, 0x64, 0x6e, 0x61, 0x6d, 0x00]
283 );
284 }
285
286 #[test]
287 fn build_subtype_out_of_range() {
288 let b = App::builder(0x91827364, "name").subtype(0x1f + 1);
289 let err = b.calculate_size().unwrap_err();
290 assert_eq!(
291 err,
292 RtcpWriteError::AppSubtypeOutOfRange {
293 subtype: 0x1f + 1,
294 max: App::SUBTYPE_MASK
295 }
296 );
297 }
298
299 #[test]
300 fn build_invalid_name_too_large() {
301 let b = App::builder(0x91827364, "name_");
302 let err = b.calculate_size().unwrap_err();
303 assert_eq!(err, RtcpWriteError::InvalidName);
304 }
305
306 #[test]
307 fn build_invalid_non_ascii_name() {
308 let b = App::builder(0x91827364, "nąm");
309 let err = b.calculate_size().unwrap_err();
310 assert_eq!(err, RtcpWriteError::InvalidName);
311 }
312
313 #[test]
314 fn build_data_len_not_32bits_multiple() {
315 let b = App::builder(0x91827364, "name").data(&[0x01, 0x02, 0x3]);
316 let err = b.calculate_size().unwrap_err();
317 assert_eq!(err, RtcpWriteError::DataLen32bitMultiple(3));
318 }
319
320 #[test]
321 fn build_padding_not_multiple_4() {
322 let b = App::builder(0x91827364, "name").padding(5);
323 let err = b.calculate_size().unwrap_err();
324 assert_eq!(err, RtcpWriteError::InvalidPadding { padding: 5 });
325 }
326}