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