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(
86 self,
87 data: impl Into<Cow<'a, [u8]>>,
88 bit_overrun: u8,
89 ) -> RpsiBuilder<'static> {
90 RpsiBuilder {
91 payload_type: self.payload_type,
92 native_bit_string: data.into().into_owned().into(),
93 native_bit_overrun: bit_overrun,
94 }
95 }
96}
97
98impl<'a> FciBuilder<'a> for RpsiBuilder<'a> {
99 fn format(&self) -> u8 {
100 Rpsi::FCI_FORMAT
101 }
102
103 fn supports_feedback_type(&self) -> FciFeedbackPacketType {
104 FciFeedbackPacketType::PAYLOAD
105 }
106}
107
108impl<'a> RtcpPacketWriter for RpsiBuilder<'a> {
109 fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
110 if self.payload_type > 127 {
111 return Err(RtcpWriteError::PayloadTypeInvalid);
112 }
113 if self.native_bit_overrun > 8
114 || self.native_bit_string.is_empty() && self.native_bit_overrun > 0
115 {
116 return Err(RtcpWriteError::PaddingBitsTooLarge);
117 }
118 Ok(pad_to_4bytes(self.native_bit_string.len()))
119 }
120
121 fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
122 let end = pad_to_4bytes(2 + self.native_bit_string.len());
123 let trailing_bits =
124 8 * (end - self.native_bit_string.len() - 2) + self.native_bit_overrun as usize;
125 buf[0] = trailing_bits as u8;
126 buf[1] = self.payload_type;
127 let mut idx = 2 + self.native_bit_string.len();
128 buf[2..idx].copy_from_slice(&self.native_bit_string);
129 if !self.native_bit_string.is_empty() {
130 let mut bitmask = 0;
131 let mut trailing_bits = self.native_bit_overrun;
132 while trailing_bits > 0 {
133 bitmask = (bitmask << 1) | 1;
134 trailing_bits -= 1;
135 }
136 buf[idx - 1] &= !bitmask;
137 }
138 while idx < end {
139 buf[idx] = 0;
140 idx += 1;
141 }
142 idx
143 }
144
145 fn get_padding(&self) -> Option<u8> {
146 None
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::feedback::PayloadFeedback;
154
155 #[test]
156 fn rpsi_build_parse() {
157 const REQ_LEN: usize = PayloadFeedback::MIN_PACKET_LEN + 4;
158 let mut data = [0; REQ_LEN];
159 let rpsi = {
160 let data = &[0xf0];
161 let fci = Rpsi::builder()
162 .payload_type(96)
163 .native_data(data.as_ref(), 4);
164 PayloadFeedback::builder_owned(fci)
165 .sender_ssrc(0x98765432)
166 .media_ssrc(0x10fedcba)
167 };
168 assert_eq!(rpsi.calculate_size().unwrap(), REQ_LEN);
169 let len = rpsi.write_into(&mut data).unwrap();
170 assert_eq!(len, REQ_LEN);
171 assert_eq!(
172 data,
173 [
174 0x83, 0xce, 0x00, 0x03, 0x98, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x0c, 0x60,
175 0xf0, 0x00
176 ]
177 );
178
179 let fb = PayloadFeedback::parse(&data).unwrap();
180
181 assert_eq!(fb.sender_ssrc(), 0x98765432);
182 assert_eq!(fb.media_ssrc(), 0x10fedcba);
183 let rpsi = fb.parse_fci::<Rpsi>().unwrap();
184 assert_eq!(rpsi.payload_type(), 96);
185 assert_eq!(rpsi.bit_string(), ([0xf0].as_ref(), 4));
186 }
187
188 #[test]
189 fn rpsi_build_parse_ref() {
190 const REQ_LEN: usize = PayloadFeedback::MIN_PACKET_LEN + 4;
191 let mut data = [0; REQ_LEN];
192 let data_fci = &[0xf0];
193 let fci = Rpsi::builder()
194 .payload_type(96)
195 .native_data(data_fci.as_ref(), 4);
196 let rpsi = PayloadFeedback::builder(&fci)
197 .sender_ssrc(0x98765432)
198 .media_ssrc(0x10fedcba);
199 assert_eq!(rpsi.calculate_size().unwrap(), REQ_LEN);
200 let len = rpsi.write_into(&mut data).unwrap();
201 assert_eq!(len, REQ_LEN);
202 assert_eq!(
203 data,
204 [
205 0x83, 0xce, 0x00, 0x03, 0x98, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x0c, 0x60,
206 0xf0, 0x00
207 ]
208 );
209
210 let fb = PayloadFeedback::parse(&data).unwrap();
211
212 assert_eq!(fb.sender_ssrc(), 0x98765432);
213 assert_eq!(fb.media_ssrc(), 0x10fedcba);
214 let rpsi = fb.parse_fci::<Rpsi>().unwrap();
215 assert_eq!(rpsi.payload_type(), 96);
216 assert_eq!(rpsi.bit_string(), ([0xf0].as_ref(), 4));
217 }
218}