Skip to content
File

Blob: firmware/vendor/str0m/src/rtp/rtcp/header.rs

rust199 lines
1#![allow(clippy::unusual_byte_groupings)]
2 
3use super::{FeedbackMessageType, PayloadType, TransportType};
4 
5pub(crate) const LEN_HEADER: usize = 4;
6 
7#[derive(Debug, PartialEq, Eq)]
8pub struct RtcpHeader {
9 pub(crate) rtcp_type: RtcpType,
10 pub(crate) feedback_message_type: FeedbackMessageType,
11 pub(crate) words_less_one: u16,
12}
13 
14impl RtcpHeader {
15 /// Type of RTCP packet. This is further divided into subtypes by
16 /// `feedback_message_type`.
17 pub fn rtcp_type(&self) -> RtcpType {
18 self.rtcp_type
19 }
20 
21 /// Subtype of RTCP message.
22 pub fn feedback_message_type(&self) -> FeedbackMessageType {
23 self.feedback_message_type
24 }
25 
26 /// Number of reports stacked in this message.
27 ///
28 /// Depending `rtcp_type` and `feedback_message_type` multiple reports of the same type can be
29 /// stacked into one RTCP packet under the same header.
30 pub fn count(&self) -> usize {
31 match self.rtcp_type {
32 RtcpType::SenderReport
33 | RtcpType::ReceiverReport
34 | RtcpType::SourceDescription
35 | RtcpType::Goodbye => self.feedback_message_type.count() as usize,
36 RtcpType::ApplicationDefined => 1,
37 RtcpType::TransportLayerFeedback => {
38 let transport_type = match self.feedback_message_type {
39 FeedbackMessageType::TransportFeedback(v) => v,
40 _ => unreachable!(),
41 };
42 
43 match transport_type {
44 TransportType::Nack => {
45 // [ssrc_sender, ssrc_media_source, fci, fci, ...]
46 let fci_length = self.length_words() * 4 - LEN_HEADER - 2 * 4;
47 
48 // each fci is one word: [pid, blp]
49 fci_length / 4
50 }
51 TransportType::TransportWide => {
52 // TODO
53 0
54 }
55 }
56 }
57 RtcpType::PayloadSpecificFeedback => {
58 let payload_type = match self.feedback_message_type {
59 FeedbackMessageType::PayloadFeedback(v) => v,
60 _ => unreachable!(),
61 };
62 
63 match payload_type {
64 PayloadType::PictureLossIndication => {
65 // PLI does not require parameters. Therefore, the length field MUST be
66 // 2, and there MUST NOT be any Feedback Control Information.
67 1
68 }
69 
70 PayloadType::FullIntraRequest => {
71 // [ssrc_sender, ssrc_media_source, fci, fci, ...]
72 let fci_length = self.length_words() * 4 - LEN_HEADER - 2 * 4;
73 
74 // each fci is two words: [ssrc, [seq_no, reserved]]
75 fci_length / 8
76 }
77 
78 _ => {
79 // PayloadType::SliceLossIndication => {},
80 // PayloadType::ReferencePictureSelectionIndication => {},
81 // PayloadType::ApplicationLayer => {},
82 0
83 }
84 }
85 }
86 RtcpType::ExtendedReport => 0,
87 }
88 }
89 
90 /// Length of entire RTCP packet (including header) in words (4 bytes).
91 pub fn length_words(&self) -> usize {
92 self.words_less_one as usize + 1
93 }
94 
95 /// Write header to buffer.
96 pub(crate) fn write_to(&self, buf: &mut [u8]) -> usize {
97 let fmt: u8 = self.feedback_message_type.into();
98 
99 buf[0] = 0b10_0_00000 | fmt;
100 buf[1] = self.rtcp_type as u8;
101 
102 buf[2..4].copy_from_slice(&self.words_less_one.to_be_bytes());
103 
104 4
105 }
106}
107 
108/// Kind of RTCP packet.
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110pub enum RtcpType {
111 /// RTCP_PT_SR
112 SenderReport = 200,
113 
114 /// RTCP_PT_RR
115 ReceiverReport = 201,
116 
117 /// RTCP_PT_SDES
118 SourceDescription = 202,
119 
120 /// RTCP_PT_BYE
121 Goodbye = 203,
122 
123 /// RTCP_PT_APP
124 ApplicationDefined = 204,
125 
126 /// RTCP_PT_RTPFB
127 // https://tools.ietf.org/html/rfc4585
128 TransportLayerFeedback = 205,
129 
130 /// RTCP_PT_PSFB
131 // https://tools.ietf.org/html/rfc4585
132 PayloadSpecificFeedback = 206,
133 
134 /// RTCP_PT_XR
135 ExtendedReport = 207,
136}
137 
138impl TryFrom<u8> for RtcpType {
139 type Error = &'static str;
140 
141 fn try_from(v: u8) -> Result<Self, Self::Error> {
142 use RtcpType::*;
143 match v {
144 200 => Ok(SenderReport), // sr
145 201 => Ok(ReceiverReport), // rr
146 202 => Ok(SourceDescription),
147 203 => Ok(Goodbye),
148 204 => Ok(ApplicationDefined),
149 205 => Ok(TransportLayerFeedback),
150 206 => Ok(PayloadSpecificFeedback),
151 207 => Ok(ExtendedReport),
152 _ => {
153 trace!("Unknown RtcpType: {}", v);
154 Err("Unknown RtcpType")
155 }
156 }
157 }
158}
159 
160impl<'a> TryFrom<&'a [u8]> for RtcpHeader {
161 type Error = &'static str;
162 
163 fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
164 if buf.len() < 4 {
165 return Err("Need 4 bytes for RTCP header");
166 }
167 
168 let version = (buf[0] & 0b11_0_00000) >> 6;
169 if version != 2 {
170 return Err("RTCP header version should be 2");
171 }
172 
173 let fmt = buf[0] & 0b00_0_11111;
174 let rtcp_type: RtcpType = buf[1].try_into()?;
175 
176 let feedback_message_type = {
177 use FeedbackMessageType::*;
178 match rtcp_type {
179 RtcpType::SenderReport => ReceptionReport(fmt),
180 RtcpType::ReceiverReport => ReceptionReport(fmt),
181 RtcpType::SourceDescription => SourceCount(fmt),
182 RtcpType::Goodbye => SourceCount(fmt),
183 RtcpType::ApplicationDefined => Subtype(fmt),
184 RtcpType::TransportLayerFeedback => TransportFeedback(fmt.try_into()?),
185 RtcpType::PayloadSpecificFeedback => PayloadFeedback(fmt.try_into()?),
186 RtcpType::ExtendedReport => NotUsed,
187 }
188 };
189 
190 let words_less_one = u16::from_be_bytes([buf[2], buf[3]]);
191 
192 Ok(RtcpHeader {
193 rtcp_type,
194 feedback_message_type,
195 words_less_one,
196 })
197 }
198}