1use super::*;
6
7fn format_time<W: std::io::Write>(w: &mut W, t: u64) -> Result<(), std::io::Error> {
8 if t == 0 {
9 write!(w, "{}", t)?;
10 } else if t % 86_400 == 0 {
11 write!(w, "{}d", t / 86_400)?;
12 } else if t % 3_600 == 0 {
13 write!(w, "{}h", t / 3_600)?;
14 } else if t % 60 == 0 {
15 write!(w, "{}m", t / 60)?;
16 } else {
17 write!(w, "{}", t)?;
18 }
19
20 Ok(())
21}
22
23impl Origin {
24 fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
25 writeln!(
26 w,
27 "o={username} {sess_id} {sess_version} {nettype} {addrtype} {unicast_address}\r",
28 username = if let Some(ref username) = self.username {
29 username
30 } else {
31 "-"
32 },
33 sess_id = self.sess_id,
34 sess_version = self.sess_version,
35 nettype = self.nettype,
36 addrtype = self.addrtype,
37 unicast_address = self.unicast_address,
38 )
39 }
40}
41
42impl Connection {
43 fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
44 writeln!(
45 w,
46 "c={nettype} {addrtype} {connection_address}\r",
47 nettype = self.nettype,
48 addrtype = self.addrtype,
49 connection_address = self.connection_address
50 )
51 }
52}
53
54impl Bandwidth {
55 fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
56 writeln!(
57 w,
58 "b={bwtype}:{bw}\r",
59 bwtype = self.bwtype,
60 bw = self.bandwidth
61 )
62 }
63}
64
65impl Time {
66 fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
67 writeln!(
68 w,
69 "t={start_time} {stop_time}\r",
70 start_time = self.start_time,
71 stop_time = self.stop_time,
72 )
73 }
74}
75
76impl Repeat {
77 fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
78 write!(w, "r=")?;
79 format_time(w, self.repeat_interval)?;
80 write!(w, " ")?;
81 format_time(w, self.active_duration)?;
82
83 for offset in &self.offsets {
84 write!(w, " ")?;
85 format_time(w, *offset)?;
86 }
87
88 writeln!(w, "\r")
89 }
90}
91
92impl TimeZone {
93 fn write<W: std::io::Write>(w: &mut W, zones: &[TimeZone]) -> Result<(), std::io::Error> {
94 write!(w, "z=")?;
95
96 let mut first = true;
97 for zone in zones {
98 if !first {
99 write!(w, " ")?;
100 }
101
102 write!(w, "{} ", zone.adjustment_time)?;
103 if zone.offset < 0 {
104 write!(w, "-")?;
105 format_time(w, (-zone.offset) as u64)?;
106 } else {
107 format_time(w, zone.offset as u64)?;
108 }
109 first = false;
110 }
111 writeln!(w, "\r")
112 }
113}
114
115impl Key {
116 fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
117 if let Some(ref encryption_key) = self.encryption_key {
118 writeln!(
119 w,
120 "k={method}:{encryption_key}\r",
121 method = self.method,
122 encryption_key = encryption_key
123 )
124 } else {
125 writeln!(w, "k={method}\r", method = self.method)
126 }
127 }
128}
129
130impl Attribute {
131 fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
132 if let Some(ref value) = self.value {
133 writeln!(
134 w,
135 "a={name}:{value}\r",
136 name = self.attribute,
137 value = value
138 )
139 } else {
140 writeln!(w, "a={name}\r", name = self.attribute)
141 }
142 }
143}
144
145impl Media {
146 fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
147 write!(w, "m={media} {port}", media = self.media, port = self.port)?;
148
149 if let Some(ref num_ports) = self.num_ports {
150 write!(w, "/{}", num_ports)?;
151 }
152 writeln!(w, " {} {}\r", self.proto, self.fmt)?;
153
154 if let Some(ref media_title) = self.media_title {
155 writeln!(w, "i={media_title}\r", media_title = media_title)?;
156 }
157
158 for connection in &self.connections {
159 connection.write(w)?;
160 }
161
162 for bandwidth in &self.bandwidths {
163 bandwidth.write(w)?;
164 }
165
166 if let Some(ref key) = self.key {
167 key.write(w)?;
168 }
169
170 for attribute in &self.attributes {
171 attribute.write(w)?;
172 }
173
174 Ok(())
175 }
176}
177
178impl Session {
179 pub fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
181 writeln!(w, "v=0\r")?;
182 self.origin.write(w)?;
183 writeln!(w, "s={session_name}\r", session_name = self.session_name)?;
184
185 if let Some(ref session_description) = self.session_description {
186 writeln!(
187 w,
188 "i={session_description}\r",
189 session_description = session_description
190 )?;
191 }
192
193 if let Some(ref uri) = self.uri {
194 writeln!(w, "u={uri}\r", uri = uri)?;
195 }
196
197 for email in &self.emails {
198 writeln!(w, "e={email}\r", email = email)?;
199 }
200
201 for phone in &self.phones {
202 writeln!(w, "p={phone}\r", phone = phone)?;
203 }
204
205 if let Some(ref connection) = self.connection {
206 connection.write(w)?;
207 }
208
209 for bandwidth in &self.bandwidths {
210 bandwidth.write(w)?;
211 }
212
213 if !self.times.is_empty() {
214 for time in &self.times {
215 time.write(w)?;
216
217 for repeat in &time.repeats {
218 repeat.write(w)?;
219 }
220 }
221 } else {
222 writeln!(w, "t=0 0\r")?;
223 }
224
225 if !self.time_zones.is_empty() {
226 TimeZone::write(w, &self.time_zones)?;
227 }
228
229 if let Some(ref key) = self.key {
230 key.write(w)?;
231 }
232
233 for attribute in &self.attributes {
234 attribute.write(w)?;
235 }
236
237 for media in &self.medias {
238 media.write(w)?;
239 }
240
241 Ok(())
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn write_sdp() {
251 let sdp = Session {
252 origin: Origin {
253 username: Some("jdoe".into()),
254 sess_id: "2890844526".into(),
255 sess_version: 2890842807,
256 nettype: "IN".into(),
257 addrtype: "IP4".into(),
258 unicast_address: "10.47.16.5".into(),
259 },
260 session_name: "SDP Seminar".into(),
261 session_description: Some("A Seminar on the session description protocol".into()),
262 uri: Some("http://www.example.com/seminars/sdp.pdf".into()),
263 emails: vec!["j.doe@example.com (Jane Doe)".into()],
264 phones: vec!["+1 617 555-6011".into()],
265 connection: Some(Connection {
266 nettype: "IN".into(),
267 addrtype: "IP4".into(),
268 connection_address: "224.2.17.12/127".into(),
269 }),
270 bandwidths: vec![Bandwidth {
271 bwtype: "AS".into(),
272 bandwidth: 128,
273 }],
274 times: vec![Time {
275 start_time: 2873397496,
276 stop_time: 2873404696,
277 repeats: vec![Repeat {
278 repeat_interval: 604800,
279 active_duration: 3600,
280 offsets: vec![0, 90000],
281 }],
282 }],
283 time_zones: vec![
284 TimeZone {
285 adjustment_time: 2882844526,
286 offset: -3600,
287 },
288 TimeZone {
289 adjustment_time: 2898848070,
290 offset: 0,
291 },
292 ],
293 key: Some(Key {
294 method: "clear".into(),
295 encryption_key: Some("1234".into()),
296 }),
297 attributes: vec![Attribute {
298 attribute: "recvonly".into(),
299 value: None,
300 }],
301 medias: vec![
302 Media {
303 media: "audio".into(),
304 port: 49170,
305 num_ports: None,
306 proto: "RTP/AVP".into(),
307 fmt: "0".into(),
308 media_title: None,
309 connections: vec![],
310 bandwidths: vec![],
311 key: None,
312 attributes: vec![],
313 },
314 Media {
315 media: "video".into(),
316 port: 51372,
317 num_ports: Some(2),
318 proto: "RTP/AVP".into(),
319 fmt: "99".into(),
320 media_title: None,
321 connections: vec![],
322 bandwidths: vec![],
323 key: None,
324 attributes: vec![Attribute {
325 attribute: "rtpmap".into(),
326 value: Some("99 h263-1998/90000".into()),
327 }],
328 },
329 ],
330 };
331
332 let mut written = vec![];
333 sdp.write(&mut written).unwrap();
334
335 let expected = "v=0\r
336o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r
337s=SDP Seminar\r
338i=A Seminar on the session description protocol\r
339u=http://www.example.com/seminars/sdp.pdf\r
340e=j.doe@example.com (Jane Doe)\r
341p=+1 617 555-6011\r
342c=IN IP4 224.2.17.12/127\r
343b=AS:128\r
344t=2873397496 2873404696\r
345r=7d 1h 0 25h\r
346z=2882844526 -1h 2898848070 0\r
347k=clear:1234\r
348a=recvonly\r
349m=audio 49170 RTP/AVP 0\r
350m=video 51372/2 RTP/AVP 99\r
351a=rtpmap:99 h263-1998/90000\r
352";
353
354 assert_eq!(String::from_utf8_lossy(&written), expected);
355 }
356}