rtc_rtcp/compound_packet/
mod.rs

1#[cfg(test)]
2mod compound_packet_test;
3
4use crate::{
5    header::*, packet::*, receiver_report::*, sender_report::*, source_description::*, util::*,
6};
7use shared::{
8    error::{Error, Result},
9    marshal::{Marshal, MarshalSize, Unmarshal},
10};
11
12use bytes::{Buf, Bytes};
13use std::any::Any;
14use std::fmt;
15
16/// A CompoundPacket is a collection of RTCP packets transmitted as a single packet with
17/// the underlying protocol (for example UDP).
18///
19/// To maximize the resolution of receiption statistics, the first Packet in a CompoundPacket
20/// must always be either a SenderReport or a ReceiverReport.  This is true even if no data
21/// has been sent or received, in which case an empty ReceiverReport must be sent, and even
22/// if the only other RTCP packet in the compound packet is a Goodbye.
23///
24/// Next, a SourceDescription containing a CNAME item must be included in each CompoundPacket
25/// to identify the source and to begin associating media for purposes such as lip-sync.
26///
27/// Other RTCP packet types may follow in any order. Packet types may appear more than once.
28#[derive(Debug, Default, PartialEq, Clone)]
29pub struct CompoundPacket(pub Vec<Box<dyn Packet>>);
30
31impl fmt::Display for CompoundPacket {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        write!(f, "{self:?}")
34    }
35}
36
37impl Packet for CompoundPacket {
38    fn header(&self) -> Header {
39        Header::default()
40    }
41
42    /// destination_ssrc returns the synchronization sources associated with this
43    /// CompoundPacket's reception report.
44    fn destination_ssrc(&self) -> Vec<u32> {
45        if self.0.is_empty() {
46            vec![]
47        } else {
48            self.0[0].destination_ssrc()
49        }
50    }
51
52    fn raw_size(&self) -> usize {
53        let mut l = 0;
54        for packet in &self.0 {
55            l += packet.marshal_size();
56        }
57        l
58    }
59
60    fn as_any(&self) -> &dyn Any {
61        self
62    }
63
64    fn equal(&self, other: &dyn Packet) -> bool {
65        other.as_any().downcast_ref::<CompoundPacket>() == Some(self)
66    }
67
68    fn cloned(&self) -> Box<dyn Packet> {
69        Box::new(self.clone())
70    }
71}
72
73impl MarshalSize for CompoundPacket {
74    fn marshal_size(&self) -> usize {
75        let l = self.raw_size();
76        // align to 32-bit boundary
77        l + get_padding_size(l)
78    }
79}
80
81impl Marshal for CompoundPacket {
82    /// Marshal encodes the CompoundPacket as binary.
83    fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
84        self.validate()?;
85
86        for packet in &self.0 {
87            let n = packet.marshal_to(buf)?;
88            buf = &mut buf[n..];
89        }
90
91        Ok(self.marshal_size())
92    }
93}
94
95impl Unmarshal for CompoundPacket {
96    fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
97    where
98        Self: Sized,
99        B: Buf,
100    {
101        let mut packets = vec![];
102
103        while raw_packet.has_remaining() {
104            let p = unmarshaller(raw_packet)?;
105            packets.push(p);
106        }
107
108        let c = CompoundPacket(packets);
109        c.validate()?;
110
111        Ok(c)
112    }
113}
114
115impl CompoundPacket {
116    /// Validate returns an error if this is not an RFC-compliant CompoundPacket.
117    pub fn validate(&self) -> Result<()> {
118        if self.0.is_empty() {
119            return Err(Error::EmptyCompound);
120        }
121
122        // SenderReport and ReceiverReport are the only types that
123        // are allowed to be the first packet in a compound datagram
124        if self.0[0].as_any().downcast_ref::<SenderReport>().is_none()
125            && self.0[0]
126                .as_any()
127                .downcast_ref::<ReceiverReport>()
128                .is_none()
129        {
130            return Err(Error::BadFirstPacket);
131        }
132
133        for pkt in &self.0[1..] {
134            // If the number of RRs exceeds 31 additional ReceiverReports
135            // can be included here.
136            if pkt.as_any().downcast_ref::<ReceiverReport>().is_some() {
137                continue;
138            // A SourceDescription containing a CNAME must be included in every
139            // CompoundPacket.
140            } else if let Some(e) = pkt.as_any().downcast_ref::<SourceDescription>() {
141                let mut has_cname = false;
142                for c in &e.chunks {
143                    for it in &c.items {
144                        if it.sdes_type == SdesType::SdesCname {
145                            has_cname = true
146                        }
147                    }
148                }
149
150                if !has_cname {
151                    return Err(Error::MissingCname);
152                }
153
154                return Ok(());
155
156            // Other packets are not permitted before the CNAME
157            } else {
158                return Err(Error::PacketBeforeCname);
159            }
160        }
161
162        // CNAME never reached
163        Err(Error::MissingCname)
164    }
165
166    /// CNAME returns the CNAME that *must* be present in every CompoundPacket
167    pub fn cname(&self) -> Result<Bytes> {
168        if self.0.is_empty() {
169            return Err(Error::EmptyCompound);
170        }
171
172        for pkt in &self.0[1..] {
173            if let Some(sdes) = pkt.as_any().downcast_ref::<SourceDescription>() {
174                for c in &sdes.chunks {
175                    for it in &c.items {
176                        if it.sdes_type == SdesType::SdesCname {
177                            return Ok(it.text.clone());
178                        }
179                    }
180                }
181            } else if pkt.as_any().downcast_ref::<ReceiverReport>().is_none() {
182                return Err(Error::PacketBeforeCname);
183            }
184        }
185
186        Err(Error::MissingCname)
187    }
188}