rtcp_types/feedback/
rpsi.rs1use std::borrow::Cow;
4
5use crate::feedback::FciFeedbackPacketType;
6use crate::utils::pad_to_4bytes;
7use crate::{prelude::*, RtcpParseError, RtcpWriteError};
8
9#[derive(Debug)]
11pub struct Rpsi<'a> {
12 data: &'a [u8],
13}
14
15impl<'a> Rpsi<'a> {
16 pub fn builder() -> RpsiBuilder<'a> {
18 RpsiBuilder::default()
19 }
20
21 pub fn payload_type(&self) -> u8 {
23 self.data[1] & 0x7f
24 }
25
26 pub fn bit_string(&self) -> (&[u8], usize) {
29 let padding_bytes = self.padding_bytes();
30 let padding_bits = self.data[0] as usize - padding_bytes * 8;
31 (&self.data[2..self.data.len() - padding_bytes], padding_bits)
32 }
33
34 fn padding_bytes(&self) -> usize {
35 (self.data[0] / 8) as usize
36 }
37}
38
39impl<'a> FciParser<'a> for Rpsi<'a> {
40 const PACKET_TYPE: FciFeedbackPacketType = FciFeedbackPacketType::PAYLOAD;
41 const FCI_FORMAT: u8 = 3;
42
43 fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
44 if data.len() < 4 {
45 return Err(RtcpParseError::Truncated {
46 expected: 4,
47 actual: data.len(),
48 });
49 }
50 let ret = Self { data };
51 if ret.padding_bytes() > data.len() - 2 {
52 return Err(RtcpParseError::Truncated {
53 expected: ret.padding_bytes() + 2,
54 actual: data.len(),
55 });
56 }
57
58 Ok(ret)
59 }
60}
61
62#[derive(Debug, Default)]
64pub struct RpsiBuilder<'a> {
65 payload_type: u8,
66 native_bit_string: Cow<'a, [u8]>,
67 native_bit_overrun: u8,
68}
69
70impl<'a> RpsiBuilder<'a> {
71 pub fn payload_type(mut self, payload_type: u8) -> Self {
73 self.payload_type = payload_type;
74 self
75 }
76
77 pub fn native_data(mut self, data: impl Into<Cow<'a, [u8]>>, bit_overrun: u8) -> Self {
80 self.native_bit_string = data.into();
81 self.native_bit_overrun = bit_overrun;
82 self
83 }
84
85 pub fn native_data_owned(
90 self,
91 data: impl Into<Cow<'a, [u8]>>,
92 bit_overrun: u8,
93 ) -> RpsiBuilder<'static> {
94 RpsiBuilder {
95 payload_type: self.payload_type,
96 native_bit_string: data.into().into_owned().into(),
97 native_bit_overrun: bit_overrun,
98 }
99 }
100}
101
102impl<'a> FciBuilder<'a> for RpsiBuilder<'a> {
103 fn format(&self) -> u8 {
104 Rpsi::FCI_FORMAT
105 }
106
107 fn supports_feedback_type(&self) -> FciFeedbackPacketType {
108 FciFeedbackPacketType::PAYLOAD
109 }
110}
111
112impl RtcpPacketWriter for RpsiBuilder<'_> {
113 fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
114 if self.payload_type > 127 {
115 return Err(RtcpWriteError::PayloadTypeInvalid);
116 }
117 if self.native_bit_overrun > 8
118 || self.native_bit_string.is_empty() && self.native_bit_overrun > 0
119 {
120 return Err(RtcpWriteError::PaddingBitsTooLarge);
121 }
122 Ok(pad_to_4bytes(self.native_bit_string.len()))
123 }
124
125 fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
126 let end = pad_to_4bytes(2 + self.native_bit_string.len());
127 let trailing_bits =
128 8 * (end - self.native_bit_string.len() - 2) + self.native_bit_overrun as usize;
129 buf[0] = trailing_bits as u8;
130 buf[1] = self.payload_type;
131 let mut idx = 2 + self.native_bit_string.len();
132 buf[2..idx].copy_from_slice(&self.native_bit_string);
133 if !self.native_bit_string.is_empty() {
134 let mut bitmask = 0;
135 let mut trailing_bits = self.native_bit_overrun;
136 while trailing_bits > 0 {
137 bitmask = (bitmask << 1) | 1;
138 trailing_bits -= 1;
139 }
140 buf[idx - 1] &= !bitmask;
141 }
142 while idx < end {
143 buf[idx] = 0;
144 idx += 1;
145 }
146 idx
147 }
148
149 fn get_padding(&self) -> Option<u8> {
150 None
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::feedback::PayloadFeedback;
158
159 #[test]
160 fn rpsi_build_parse() {
161 const REQ_LEN: usize = PayloadFeedback::MIN_PACKET_LEN + 4;
162 let mut data = [0; REQ_LEN];
163 let rpsi = {
164 let data = &[0xf0];
165 let fci = Rpsi::builder()
166 .payload_type(96)
167 .native_data(data.as_ref(), 4);
168 PayloadFeedback::builder_owned(fci)
169 .sender_ssrc(0x98765432)
170 .media_ssrc(0x10fedcba)
171 };
172 assert_eq!(rpsi.calculate_size().unwrap(), REQ_LEN);
173 let len = rpsi.write_into(&mut data).unwrap();
174 assert_eq!(len, REQ_LEN);
175 assert_eq!(
176 data,
177 [
178 0x83, 0xce, 0x00, 0x03, 0x98, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x0c, 0x60,
179 0xf0, 0x00
180 ]
181 );
182
183 let fb = PayloadFeedback::parse(&data).unwrap();
184
185 assert_eq!(fb.sender_ssrc(), 0x98765432);
186 assert_eq!(fb.media_ssrc(), 0x10fedcba);
187 let rpsi = fb.parse_fci::<Rpsi>().unwrap();
188 assert_eq!(rpsi.payload_type(), 96);
189 assert_eq!(rpsi.bit_string(), ([0xf0].as_ref(), 4));
190 }
191
192 #[test]
193 fn rpsi_build_parse_ref() {
194 const REQ_LEN: usize = PayloadFeedback::MIN_PACKET_LEN + 4;
195 let mut data = [0; REQ_LEN];
196 let data_fci = &[0xf0];
197 let fci = Rpsi::builder()
198 .payload_type(96)
199 .native_data(data_fci.as_ref(), 4);
200 let rpsi = PayloadFeedback::builder(&fci)
201 .sender_ssrc(0x98765432)
202 .media_ssrc(0x10fedcba);
203 assert_eq!(rpsi.calculate_size().unwrap(), REQ_LEN);
204 let len = rpsi.write_into(&mut data).unwrap();
205 assert_eq!(len, REQ_LEN);
206 assert_eq!(
207 data,
208 [
209 0x83, 0xce, 0x00, 0x03, 0x98, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x0c, 0x60,
210 0xf0, 0x00
211 ]
212 );
213
214 let fb = PayloadFeedback::parse(&data).unwrap();
215
216 assert_eq!(fb.sender_ssrc(), 0x98765432);
217 assert_eq!(fb.media_ssrc(), 0x10fedcba);
218 let rpsi = fb.parse_fci::<Rpsi>().unwrap();
219 assert_eq!(rpsi.payload_type(), 96);
220 assert_eq!(rpsi.bit_string(), ([0xf0].as_ref(), 4));
221 }
222}