rtcp_types/feedback/
sli.rs1use crate::feedback::FciFeedbackPacketType;
4use crate::{prelude::*, RtcpParseError, RtcpWriteError};
5
6#[derive(Debug)]
8pub struct Sli<'a> {
9 data: &'a [u8],
10}
11
12impl Sli<'_> {
13 pub fn lost_macroblocks(&self) -> impl Iterator<Item = MacroBlockEntry> + '_ {
15 MacroBlockIter {
16 data: self.data,
17 i: 0,
18 }
19 }
20
21 pub fn builder() -> SliBuilder {
23 SliBuilder { lost_mbs: vec![] }
24 }
25}
26
27impl<'a> FciParser<'a> for Sli<'a> {
28 const PACKET_TYPE: FciFeedbackPacketType = FciFeedbackPacketType::PAYLOAD;
29 const FCI_FORMAT: u8 = 2;
30
31 fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
32 if data.len() < 4 {
33 return Err(RtcpParseError::Truncated {
34 expected: 4,
35 actual: data.len(),
36 });
37 }
38 Ok(Self { data })
39 }
40}
41
42struct MacroBlockIter<'a> {
43 data: &'a [u8],
44 i: usize,
45}
46
47impl Iterator for MacroBlockIter<'_> {
48 type Item = MacroBlockEntry;
49
50 fn next(&mut self) -> Option<Self::Item> {
51 if self.i + 3 > self.data.len() {
52 return None;
53 }
54 let data = [
55 self.data[self.i],
56 self.data[self.i + 1],
57 self.data[self.i + 2],
58 self.data[self.i + 3],
59 ];
60 self.i += 4;
61 Some(MacroBlockEntry::decode(data))
62 }
63}
64
65#[derive(Debug, PartialEq, Eq, Copy, Clone)]
67pub struct MacroBlockEntry {
68 start: u16,
69 count: u16,
70 picture_id: u8,
71}
72
73impl MacroBlockEntry {
74 fn encode(&self) -> [u8; 4] {
75 let mut ret = [0; 4];
76 ret[0] = ((self.start & 0x1fe0) >> 5) as u8;
77 ret[1] = ((self.start & 0x1f) << 3) as u8 | ((self.count & 0x1c00) >> 10) as u8;
78 ret[2] = ((self.count & 0x03fc) >> 2) as u8;
79 ret[3] = ((self.count & 0x0003) as u8) << 6 | self.picture_id & 0x3f;
80 ret
81 }
82
83 fn decode(data: [u8; 4]) -> Self {
84 let start = (data[0] as u16) << 5 | (data[1] as u16 & 0xf8) >> 3;
85 let count =
86 ((data[1] & 0x07) as u16) << 10 | (data[2] as u16) << 2 | (data[3] as u16 & 0xc0) >> 6;
87 let picture_id = data[3] & 0x3f;
88 Self {
89 start,
90 count,
91 picture_id,
92 }
93 }
94}
95
96#[derive(Debug)]
98pub struct SliBuilder {
99 lost_mbs: Vec<MacroBlockEntry>,
100}
101
102impl SliBuilder {
103 pub fn add_lost_macroblock(
105 mut self,
106 start_macroblock: u16,
107 count_macroblocks: u16,
108 picture_id: u8,
109 ) -> Self {
110 self.lost_mbs.push(MacroBlockEntry {
111 start: start_macroblock,
112 count: count_macroblocks,
113 picture_id,
114 });
115 self
116 }
117}
118
119impl FciBuilder<'_> for SliBuilder {
120 fn format(&self) -> u8 {
121 2
122 }
123
124 fn supports_feedback_type(&self) -> FciFeedbackPacketType {
125 FciFeedbackPacketType::PAYLOAD
126 }
127}
128
129impl RtcpPacketWriter for SliBuilder {
130 fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
131 Ok(4 * self.lost_mbs.len())
132 }
133
134 fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
135 let mut idx = 0;
136 for entry in self.lost_mbs.iter() {
137 let encoded = entry.encode();
138 buf[idx] = encoded[0];
139 buf[idx + 1] = encoded[1];
140 buf[idx + 2] = encoded[2];
141 buf[idx + 3] = encoded[3];
142 idx += 4;
143 }
144 idx
145 }
146
147 fn get_padding(&self) -> Option<u8> {
148 None
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use crate::feedback::PayloadFeedback;
156
157 #[test]
158 fn macroblock_entries() {
159 let mut start = 0;
160 loop {
161 let mut count = 0;
162 loop {
163 for picture_id in 0..=0x3f {
164 let mbe = MacroBlockEntry {
165 start,
166 count,
167 picture_id,
168 };
169 let other = MacroBlockEntry::decode(mbe.encode());
170 assert_eq!(mbe, other);
171 }
172 count = (count << 1) | 1;
173 if count > 0x1fff {
174 break;
175 }
176 }
177 start = (start << 1) | 1;
178 if start > 0x1fff {
179 break;
180 }
181 }
182 }
183
184 #[test]
185 fn sli_build_parse() {
186 const REQ_LEN: usize = PayloadFeedback::MIN_PACKET_LEN + 4;
187 let mut data = [0; REQ_LEN];
188 let sli = {
189 let fci = Sli::builder().add_lost_macroblock(0x1234, 0x0987, 0x25);
190 PayloadFeedback::builder_owned(fci)
191 .sender_ssrc(0x98765432)
192 .media_ssrc(0x10fedcba)
193 };
194 assert_eq!(sli.calculate_size().unwrap(), REQ_LEN);
195 let len = sli.write_into(&mut data).unwrap();
196 assert_eq!(len, REQ_LEN);
197 assert_eq!(
198 data,
199 [
200 0x82, 0xce, 0x00, 0x03, 0x98, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x91, 0xa2,
201 0x61, 0xe5
202 ]
203 );
204
205 let fb = PayloadFeedback::parse(&data).unwrap();
206
207 assert_eq!(fb.sender_ssrc(), 0x98765432);
208 assert_eq!(fb.media_ssrc(), 0x10fedcba);
209 let sli = fb.parse_fci::<Sli>().unwrap();
210 let mut mb_iter = sli.lost_macroblocks();
211 assert_eq!(
212 mb_iter.next(),
213 Some(MacroBlockEntry {
214 start: 0x1234,
215 count: 0x987,
216 picture_id: 0x25
217 })
218 );
219 assert_eq!(mb_iter.next(), None);
220 }
221
222 #[test]
223 fn sli_build_ref() {
224 const REQ_LEN: usize = PayloadFeedback::MIN_PACKET_LEN + 4;
225 let mut data = [0; REQ_LEN];
226 let fci = Sli::builder().add_lost_macroblock(0x1234, 0x0987, 0x25);
227 let sli = PayloadFeedback::builder(&fci)
228 .sender_ssrc(0x98765432)
229 .media_ssrc(0x10fedcba);
230 assert_eq!(sli.calculate_size().unwrap(), REQ_LEN);
231 let len = sli.write_into(&mut data).unwrap();
232 assert_eq!(len, REQ_LEN);
233 assert_eq!(
234 data,
235 [
236 0x82, 0xce, 0x00, 0x03, 0x98, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x91, 0xa2,
237 0x61, 0xe5
238 ]
239 );
240 }
241}