Skip to main content

rtc_rtcp/extended_report/
mod.rs

1#[cfg(test)]
2mod extended_report_test;
3
4pub mod dlrr;
5pub mod prt;
6pub mod rle;
7pub mod rrt;
8pub mod ssr;
9pub mod unknown;
10pub mod vm;
11
12pub use dlrr::{DLRRReport, DLRRReportBlock};
13pub use prt::PacketReceiptTimesReportBlock;
14pub use rle::{Chunk, ChunkType, DuplicateRLEReportBlock, LossRLEReportBlock, RLEReportBlock};
15pub use rrt::ReceiverReferenceTimeReportBlock;
16pub use ssr::{StatisticsSummaryReportBlock, TTLorHopLimitType};
17pub use unknown::UnknownReportBlock;
18pub use vm::VoIPMetricsReportBlock;
19
20use crate::Packet;
21use crate::header::{HEADER_LENGTH, Header, PacketType, SSRC_LENGTH};
22use crate::util::{get_padding_size, put_padding};
23use bytes::{Buf, BufMut, Bytes};
24use shared::{
25    error::{Error, Result},
26    marshal::{Marshal, MarshalSize, Unmarshal},
27};
28use std::any::Any;
29use std::fmt;
30
31const XR_HEADER_LENGTH: usize = 4;
32
33/// BlockType specifies the type of report in a report block
34/// Extended Report block types from RFC 3611.
35#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
36pub enum BlockType {
37    #[default]
38    Unknown = 0,
39    LossRLE = 1,               // RFC 3611, section 4.1
40    DuplicateRLE = 2,          // RFC 3611, section 4.2
41    PacketReceiptTimes = 3,    // RFC 3611, section 4.3
42    ReceiverReferenceTime = 4, // RFC 3611, section 4.4
43    DLRR = 5,                  // RFC 3611, section 4.5
44    StatisticsSummary = 6,     // RFC 3611, section 4.6
45    VoIPMetrics = 7,           // RFC 3611, section 4.7
46}
47
48impl From<u8> for BlockType {
49    fn from(v: u8) -> Self {
50        match v {
51            1 => BlockType::LossRLE,
52            2 => BlockType::DuplicateRLE,
53            3 => BlockType::PacketReceiptTimes,
54            4 => BlockType::ReceiverReferenceTime,
55            5 => BlockType::DLRR,
56            6 => BlockType::StatisticsSummary,
57            7 => BlockType::VoIPMetrics,
58            _ => BlockType::Unknown,
59        }
60    }
61}
62
63/// converts the Extended report block types into readable strings
64impl fmt::Display for BlockType {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        let s = match *self {
67            BlockType::LossRLE => "LossRLEReportBlockType",
68            BlockType::DuplicateRLE => "DuplicateRLEReportBlockType",
69            BlockType::PacketReceiptTimes => "PacketReceiptTimesReportBlockType",
70            BlockType::ReceiverReferenceTime => "ReceiverReferenceTimeReportBlockType",
71            BlockType::DLRR => "DLRRReportBlockType",
72            BlockType::StatisticsSummary => "StatisticsSummaryReportBlockType",
73            BlockType::VoIPMetrics => "VoIPMetricsReportBlockType",
74            _ => "UnknownReportBlockType",
75        };
76        write!(f, "{s}")
77    }
78}
79
80/// TypeSpecificField as described in RFC 3611 section 4.5. In typical
81/// cases, users of ExtendedReports shouldn't need to access this,
82/// and should instead use the corresponding fields in the actual
83/// report blocks themselves.
84pub type TypeSpecificField = u8;
85
86/// XRHeader defines the common fields that must appear at the start
87/// of each report block. In typical cases, users of ExtendedReports
88/// shouldn't need to access this. For locally-constructed report
89/// blocks, these values will not be accurate until the corresponding
90/// packet is marshaled.
91#[derive(Debug, Default, PartialEq, Eq, Clone)]
92pub struct XRHeader {
93    pub block_type: BlockType,
94    pub type_specific: TypeSpecificField,
95    pub block_length: u16,
96}
97
98impl MarshalSize for XRHeader {
99    fn marshal_size(&self) -> usize {
100        XR_HEADER_LENGTH
101    }
102}
103
104impl Marshal for XRHeader {
105    /// marshal_to encodes the ExtendedReport in binary
106    fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
107        if buf.remaining_mut() < XR_HEADER_LENGTH {
108            return Err(Error::BufferTooShort);
109        }
110
111        buf.put_u8(self.block_type as u8);
112        buf.put_u8(self.type_specific);
113        buf.put_u16(self.block_length);
114
115        Ok(XR_HEADER_LENGTH)
116    }
117}
118
119impl Unmarshal for XRHeader {
120    /// Unmarshal decodes the ExtendedReport from binary
121    fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
122    where
123        Self: Sized,
124        B: Buf,
125    {
126        if raw_packet.remaining() < XR_HEADER_LENGTH {
127            return Err(Error::PacketTooShort);
128        }
129
130        let block_type: BlockType = raw_packet.get_u8().into();
131        let type_specific = raw_packet.get_u8();
132        let block_length = raw_packet.get_u16();
133
134        Ok(XRHeader {
135            block_type,
136            type_specific,
137            block_length,
138        })
139    }
140}
141/// The ExtendedReport packet is an Implementation of RTCP Extended
142/// reports defined in RFC 3611. It is used to convey detailed
143/// information about an RTP stream. Each packet contains one or
144/// more report blocks, each of which conveys a different kind of
145/// information.
146///
147///  0                   1                   2                   3
148///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
149/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
150/// |V=2|P|reserved |   PT=XR=207   |             length            |
151/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
152/// |                              ssrc                             |
153/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
154/// :                         report blocks                         :
155/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
156#[derive(Debug, PartialEq, Default, Clone)]
157pub struct ExtendedReport {
158    pub sender_ssrc: u32,
159    pub reports: Vec<Box<dyn Packet>>,
160}
161
162impl fmt::Display for ExtendedReport {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        write!(f, "{self:?}")
165    }
166}
167
168impl Packet for ExtendedReport {
169    /// Header returns the Header associated with this packet.
170    fn header(&self) -> Header {
171        Header {
172            padding: get_padding_size(self.raw_size()) != 0,
173            count: 0,
174            packet_type: PacketType::ExtendedReport,
175            length: ((self.marshal_size() / 4) - 1) as u16,
176        }
177    }
178
179    /// destination_ssrc returns an array of ssrc values that this packet refers to.
180    fn destination_ssrc(&self) -> Vec<u32> {
181        let mut ssrc = vec![];
182        for p in &self.reports {
183            ssrc.extend(p.destination_ssrc());
184        }
185        ssrc
186    }
187
188    fn raw_size(&self) -> usize {
189        let mut reps_length = 0;
190        for rep in &self.reports {
191            reps_length += rep.marshal_size();
192        }
193        HEADER_LENGTH + SSRC_LENGTH + reps_length
194    }
195
196    fn as_any(&self) -> &dyn Any {
197        self
198    }
199
200    fn equal(&self, other: &dyn Packet) -> bool {
201        other.as_any().downcast_ref::<ExtendedReport>() == Some(self)
202    }
203
204    fn cloned(&self) -> Box<dyn Packet> {
205        Box::new(self.clone())
206    }
207}
208
209impl MarshalSize for ExtendedReport {
210    fn marshal_size(&self) -> usize {
211        let l = self.raw_size();
212        // align to 32-bit boundary
213        l + get_padding_size(l)
214    }
215}
216
217impl Marshal for ExtendedReport {
218    /// marshal_to encodes the ExtendedReport in binary
219    fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
220        if buf.remaining_mut() < self.marshal_size() {
221            return Err(Error::BufferTooShort);
222        }
223
224        let h = self.header();
225        let n = h.marshal_to(buf)?;
226        buf = &mut buf[n..];
227
228        buf.put_u32(self.sender_ssrc);
229
230        for report in &self.reports {
231            let n = report.marshal_to(buf)?;
232            buf = &mut buf[n..];
233        }
234
235        if h.padding {
236            put_padding(buf, self.raw_size());
237        }
238
239        Ok(self.marshal_size())
240    }
241}
242
243impl Unmarshal for ExtendedReport {
244    /// Unmarshal decodes the ExtendedReport from binary
245    fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
246    where
247        Self: Sized,
248        B: Buf,
249    {
250        let raw_packet_len = raw_packet.remaining();
251        if raw_packet_len < (HEADER_LENGTH + SSRC_LENGTH) {
252            return Err(Error::PacketTooShort);
253        }
254
255        let header = Header::unmarshal(raw_packet)?;
256        if header.packet_type != PacketType::ExtendedReport {
257            return Err(Error::WrongType);
258        }
259
260        let sender_ssrc = raw_packet.get_u32();
261
262        let mut offset = HEADER_LENGTH + SSRC_LENGTH;
263        let mut reports = vec![];
264        while raw_packet.remaining() > 0 {
265            if offset + XR_HEADER_LENGTH > raw_packet_len {
266                return Err(Error::PacketTooShort);
267            }
268
269            let block_type: BlockType = raw_packet.chunk()[0].into();
270            let report: Box<dyn Packet> = match block_type {
271                BlockType::LossRLE => Box::new(LossRLEReportBlock::unmarshal(raw_packet)?),
272                BlockType::DuplicateRLE => {
273                    Box::new(DuplicateRLEReportBlock::unmarshal(raw_packet)?)
274                }
275                BlockType::PacketReceiptTimes => {
276                    Box::new(PacketReceiptTimesReportBlock::unmarshal(raw_packet)?)
277                }
278                BlockType::ReceiverReferenceTime => {
279                    Box::new(ReceiverReferenceTimeReportBlock::unmarshal(raw_packet)?)
280                }
281                BlockType::DLRR => Box::new(DLRRReportBlock::unmarshal(raw_packet)?),
282                BlockType::StatisticsSummary => {
283                    Box::new(StatisticsSummaryReportBlock::unmarshal(raw_packet)?)
284                }
285                BlockType::VoIPMetrics => Box::new(VoIPMetricsReportBlock::unmarshal(raw_packet)?),
286                _ => Box::new(UnknownReportBlock::unmarshal(raw_packet)?),
287            };
288
289            offset += report.marshal_size();
290            reports.push(report);
291        }
292
293        Ok(ExtendedReport {
294            sender_ssrc,
295            reports,
296        })
297    }
298}