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