rtp_parse/rtcp/
rtcp_sdes.rs1use std::str::from_utf8;
2
3use anyhow::{Context, Result};
4use bit_cursor::{
5 bit_read_exts::BitReadExts, bit_write_exts::BitWriteExts, byte_order::NetworkOrder,
6};
7
8use crate::{util::consume_padding, PacketBuffer, PacketBufferMut};
9
10use super::rtcp_header::{write_rtcp_header, RtcpHeader};
11
12#[derive(Debug)]
39pub struct RtcpSdesPacket {
40 pub header: RtcpHeader,
41 pub chunks: Vec<SdesChunk>,
42}
43
44impl RtcpSdesPacket {
45 pub const PT: u8 = 202;
46}
47
48pub fn read_rtcp_sdes<B: PacketBuffer>(buf: &mut B, header: RtcpHeader) -> Result<RtcpSdesPacket> {
49 let num_chunks = header.report_count;
50 let chunks = (0u8..num_chunks.into())
51 .map(|i| read_sdes_chunk(buf).with_context(|| format!("chunk {i}")))
52 .collect::<Result<Vec<SdesChunk>>>()
53 .context("sdes chunks")?;
54
55 Ok(RtcpSdesPacket { header, chunks })
56}
57
58pub fn write_rtcp_sdes<B: PacketBufferMut>(buf: &mut B, rtcp_sdes: &RtcpSdesPacket) -> Result<()> {
59 write_rtcp_header(buf, &rtcp_sdes.header).context("header")?;
60 rtcp_sdes
61 .chunks
62 .iter()
63 .enumerate()
64 .map(|(i, chunk)| write_sdes_chunk(buf, chunk).with_context(|| format!("chunk {i}")))
65 .collect::<Result<Vec<()>>>()
66 .context("chunks")?;
67
68 Ok(())
69}
70
71#[derive(Debug)]
77pub enum SdesItem {
78 Empty,
79 Cname(String),
80 Unknown { item_type: u8, data: Vec<u8> },
81}
82
83pub fn read_sdes_item<R: PacketBuffer>(buf: &mut R) -> Result<SdesItem> {
84 let id = buf.read_u8().context("id")?;
85 if id == 0 {
86 return Ok(SdesItem::Empty);
87 }
88 let length = buf.read_u8().context("length")? as usize;
89 let mut value_bytes = vec![0u8; length];
90 buf.read_exact(&mut value_bytes).context("value")?;
91 match id {
92 1 => Ok(SdesItem::Cname(from_utf8(&value_bytes)?.to_owned())),
93 t => Ok(SdesItem::Unknown {
94 item_type: t,
95 data: value_bytes.to_vec(),
96 }),
97 }
98}
99
100pub fn write_sdes_item<W: PacketBufferMut>(buf: &mut W, sdes_item: &SdesItem) -> Result<()> {
101 match sdes_item {
102 SdesItem::Empty => {
103 buf.write_u8(0).context("id")?;
104 }
105 SdesItem::Cname(value) => {
106 buf.write_u8(1).context("id")?;
107 let bytes = value.as_bytes();
108 buf.write_u8(bytes.len() as u8).context("length")?;
109 buf.write(bytes).context("value")?;
110 }
111 SdesItem::Unknown { item_type, data } => {
112 buf.write_u8(*item_type).context("id")?;
113 buf.write(data).context("value")?;
114 }
115 }
116
117 Ok(())
118}
119
120#[derive(Debug)]
121pub struct SdesChunk {
122 pub ssrc: u32,
123 pub sdes_items: Vec<SdesItem>,
124}
125
126pub fn read_sdes_chunk<R: PacketBuffer>(buf: &mut R) -> Result<SdesChunk> {
127 let ssrc = buf.read_u32::<NetworkOrder>().context("ssrc")?;
128 let mut sdes_items: Vec<SdesItem> = Vec::new();
129 loop {
130 let sdes_item = read_sdes_item(buf).context("item")?;
131 if matches!(sdes_item, SdesItem::Empty) {
132 break;
133 }
134 sdes_items.push(sdes_item);
135 }
136
137 consume_padding(buf);
138
139 Ok(SdesChunk { ssrc, sdes_items })
140}
141
142pub fn write_sdes_chunk<W: PacketBufferMut>(buf: &mut W, sdes_chunk: &SdesChunk) -> Result<()> {
143 buf.write_u32::<NetworkOrder>(sdes_chunk.ssrc)
144 .context("ssrc")?;
145 sdes_chunk
146 .sdes_items
147 .iter()
148 .enumerate()
149 .map(|(i, sdes_item)| {
150 write_sdes_item(buf, sdes_item).with_context(|| format!("sdes item {i}"))
151 })
152 .collect::<Result<Vec<()>>>()
153 .context("sdes items")?;
154
155 write_sdes_item(buf, &SdesItem::Empty).context("empty item")?;
156
157 Ok(())
162}
163
164#[cfg(test)]
165mod tests {
166 use bit_cursor::{
167 bit_cursor::BitCursor,
168 nsw_types::{u2, u5},
169 };
170 use bitvec::{order::Msb0, vec::BitVec};
171
172 use super::*;
173
174 fn create_cname_item_bytes(str: &str) -> Vec<u8> {
175 let data = str.bytes();
176 let mut item_data = vec![0x1, data.len() as u8];
177 item_data.extend(data.collect::<Vec<u8>>());
178
179 item_data
180 }
181
182 #[test]
183 fn test_read_sdes_item_success() {
184 let str = "hello, world!";
185 let item_data = create_cname_item_bytes(str);
186
187 let mut buf = BitCursor::new(BitVec::<u8, Msb0>::from_vec(item_data));
188 let sdes_item = read_sdes_item(&mut buf).unwrap();
189 match sdes_item {
190 SdesItem::Cname(v) => assert_eq!(v, str),
191 _ => panic!("Wrong SdesItem type"),
192 }
193 }
194
195 #[test]
196 fn test_read_sdes_item_bad_data() {
197 let data: Vec<u8> = vec![0xDE, 0xAD, 0xBE, 0xEF];
198 let mut item_data = vec![0x1, data.len() as u8];
199 item_data.extend(data);
200
201 let mut buf = BitCursor::new(BitVec::<u8, Msb0>::from_vec(item_data));
202 let res = read_sdes_item(&mut buf);
203 assert!(res.is_err());
204 }
205
206 #[test]
207 fn test_read_sdes() {
208 let header = RtcpHeader {
209 version: u2::new(2),
210 has_padding: false,
211 report_count: u5::new(1),
212 packet_type: 202,
213 length_field: 6,
214 };
215 #[rustfmt::skip]
216 let sdes_chunk = vec![
217 0xa8, 0x9c, 0x2a, 0xc5,
219 0x01, 0x10, 0x36, 0x45, 0x45, 0x4e, 0x42, 0x48, 0x2b, 0x70, 0x46, 0x71, 0x74, 0x70, 0x54, 0x36, 0x53, 0x46,
221 0x00,
223 ];
224 let mut cursor = BitCursor::new(BitVec::<u8, Msb0>::from_vec(sdes_chunk));
225
226 let sdes = read_rtcp_sdes(&mut cursor, header).expect("sdes");
227 assert_eq!(sdes.chunks.len(), 1);
228 let chunk = sdes.chunks.first().expect("sdes chunk");
229 assert_eq!(chunk.ssrc, 2828806853);
230 }
231
232 }