1use std::{
4 fmt::{self, Display, Formatter},
5 str::FromStr,
6};
7
8use str_reader::StringReader;
9
10use crate::{
11 attribute::{Attribute, Attributes},
12 bandwidth::Bandwidth,
13 connection::ConnectionInfo,
14 key::EncryptionKey,
15 parser::{FromSessionDescriptionLines, SessionDescriptionLines},
16 ParseError,
17};
18
19#[derive(Clone)]
21pub struct MediaDescriptionBuilder {
22 inner: MediaDescription,
23}
24
25impl MediaDescriptionBuilder {
26 const fn new(media_type: String, port: u16, protocol: String) -> Self {
28 let inner = MediaDescription {
29 media_type,
30 port,
31 port_count: None,
32 protocol,
33 formats: Vec::new(),
34 title: None,
35 bandwidth: Vec::new(),
36 key: None,
37 connection: Vec::new(),
38 attributes: Attributes::new(),
39 };
40
41 Self { inner }
42 }
43
44 #[inline]
46 pub fn port_count(&mut self, port_count: u16) -> &mut Self {
47 self.inner.port_count = Some(port_count);
48 self
49 }
50
51 #[inline]
53 pub fn format<T>(&mut self, fmt: T) -> &mut Self
54 where
55 T: ToString,
56 {
57 self.inner.formats.push(fmt.to_string());
58 self
59 }
60
61 #[inline]
63 pub fn title<T>(&mut self, title: T) -> &mut Self
64 where
65 T: ToString,
66 {
67 self.inner.title = Some(title.to_string());
68 self
69 }
70
71 #[inline]
73 pub fn bandwidth(&mut self, bandwidth: Bandwidth) -> &mut Self {
74 self.inner.bandwidth.push(bandwidth);
75 self
76 }
77
78 #[inline]
80 pub fn key(&mut self, key: EncryptionKey) -> &mut Self {
81 self.inner.key = Some(key);
82 self
83 }
84
85 #[inline]
87 pub fn connection(&mut self, connection: ConnectionInfo) -> &mut Self {
88 self.inner.connection.push(connection);
89 self
90 }
91
92 #[inline]
94 pub fn flag<T>(&mut self, name: T) -> &mut Self
95 where
96 T: ToString,
97 {
98 self.inner.attributes.push(Attribute::new_flag(name));
99 self
100 }
101
102 #[inline]
104 pub fn attribute<T, U>(&mut self, name: T, value: U) -> &mut Self
105 where
106 T: ToString,
107 U: ToString,
108 {
109 self.inner
110 .attributes
111 .push(Attribute::new_attribute(name, value));
112 self
113 }
114
115 #[inline]
117 pub fn build(self) -> MediaDescription {
118 self.inner
119 }
120}
121
122pub type MediaFormat = String;
124
125#[derive(Clone)]
127pub struct MediaDescription {
128 media_type: String,
129 port: u16,
130 port_count: Option<u16>,
131 protocol: String,
132 formats: Vec<MediaFormat>,
133 title: Option<String>,
134 bandwidth: Vec<Bandwidth>,
135 key: Option<EncryptionKey>,
136 connection: Vec<ConnectionInfo>,
137 attributes: Attributes,
138}
139
140impl MediaDescription {
141 const fn new() -> Self {
143 Self {
144 media_type: String::new(),
145 port: 0,
146 port_count: None,
147 protocol: String::new(),
148 formats: Vec::new(),
149 title: None,
150 bandwidth: Vec::new(),
151 key: None,
152 connection: Vec::new(),
153 attributes: Attributes::new(),
154 }
155 }
156
157 #[inline]
159 pub fn builder<T, U>(media_type: T, port: u16, protocol: U) -> MediaDescriptionBuilder
160 where
161 T: ToString,
162 U: ToString,
163 {
164 MediaDescriptionBuilder::new(media_type.to_string(), port, protocol.to_string())
165 }
166
167 #[inline]
169 pub fn media_type(&self) -> &str {
170 &self.media_type
171 }
172
173 #[inline]
175 pub fn port(&self) -> u16 {
176 self.port
177 }
178
179 #[inline]
181 pub fn port_count(&self) -> Option<u16> {
182 self.port_count
183 }
184
185 #[inline]
187 pub fn protocol(&self) -> &str {
188 &self.protocol
189 }
190
191 #[inline]
193 pub fn formats(&self) -> &[MediaFormat] {
194 &self.formats
195 }
196
197 #[inline]
199 pub fn title(&self) -> Option<&str> {
200 self.title.as_deref()
201 }
202
203 #[inline]
205 pub fn bandwidth(&self) -> &[Bandwidth] {
206 &self.bandwidth
207 }
208
209 #[inline]
211 pub fn encryption_key(&self) -> Option<&EncryptionKey> {
212 self.key.as_ref()
213 }
214
215 #[inline]
217 pub fn connection(&self) -> &[ConnectionInfo] {
218 &self.connection
219 }
220
221 #[inline]
223 pub fn attributes(&self) -> &Attributes {
224 &self.attributes
225 }
226}
227
228impl Display for MediaDescription {
229 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
230 write!(f, "m={} {}", self.media_type, self.port)?;
231
232 if let Some(count) = self.port_count {
233 write!(f, "/{count}")?;
234 }
235
236 write!(f, " {}", self.protocol)?;
237
238 for fmt in &self.formats {
239 write!(f, " {fmt}")?;
240 }
241
242 f.write_str("\r\n")?;
243
244 if let Some(title) = self.title.as_ref() {
245 write!(f, "i={title}\r\n")?;
246 }
247
248 for connection in &self.connection {
249 write!(f, "c={connection}\r\n")?;
250 }
251
252 for bw in &self.bandwidth {
253 write!(f, "b={bw}\r\n")?;
254 }
255
256 if let Some(k) = self.key.as_ref() {
257 write!(f, "k={k}\r\n")?;
258 }
259
260 for attr in self.attributes.iter() {
261 write!(f, "a={attr}\r\n")?;
262 }
263
264 Ok(())
265 }
266}
267
268impl FromSessionDescriptionLines for MediaDescription {
269 fn from_sdp_lines(lines: &mut SessionDescriptionLines) -> Result<Self, ParseError> {
270 let (t, v) = lines.current().unwrap();
271
272 debug_assert_eq!(t, 'm');
273
274 let mut mdp = MediaDescription::new();
275
276 let mut reader = StringReader::new(v);
277
278 mdp.media_type = String::from(reader.read_word());
279
280 mdp.port = reader.read_u16()?;
281
282 reader.skip_whitespace();
283
284 if reader.current_char() == Some('/') {
285 reader.skip_char();
286
287 mdp.port_count = Some(reader.read_u16()?);
288 }
289
290 mdp.protocol = String::from(reader.read_word());
291
292 loop {
293 reader.skip_whitespace();
294
295 if reader.is_empty() {
296 break;
297 }
298
299 mdp.formats.push(String::from(reader.read_word()));
300 }
301
302 lines.next()?;
303
304 while let Some((t, _)) = lines.current() {
305 match t {
306 'm' => break,
307 'i' => mdp.title = Some(lines.parse()?),
308 'c' => mdp.connection.push(lines.parse()?),
309 'b' => mdp.bandwidth.push(lines.parse()?),
310 'k' => mdp.key = Some(lines.parse()?),
311 'a' => mdp.attributes.push(lines.parse()?),
312 _ => {
313 return Err(ParseError::with_msg(format!(
314 "unknown media description field: {t}",
315 )))
316 }
317 }
318 }
319
320 Ok(mdp)
321 }
322}
323
324impl FromStr for MediaDescription {
325 type Err = ParseError;
326
327 fn from_str(s: &str) -> Result<Self, Self::Err> {
328 let mut lines = SessionDescriptionLines::new(s)?;
329
330 MediaDescription::from_sdp_lines(&mut lines)
331 }
332}