Skip to main content

sdp_types/
writer.rs

1// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
2//
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5use 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")?;
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    /// Write the SDP session description to a `std::io::Write`.
180    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!(w, "i={session_description}\r")?;
187        }
188
189        if let Some(ref uri) = self.uri {
190            writeln!(w, "u={uri}\r")?;
191        }
192
193        for email in &self.emails {
194            writeln!(w, "e={email}\r")?;
195        }
196
197        for phone in &self.phones {
198            writeln!(w, "p={phone}\r")?;
199        }
200
201        if let Some(ref connection) = self.connection {
202            connection.write(w)?;
203        }
204
205        for bandwidth in &self.bandwidths {
206            bandwidth.write(w)?;
207        }
208
209        if !self.times.is_empty() {
210            for time in &self.times {
211                time.write(w)?;
212
213                for repeat in &time.repeats {
214                    repeat.write(w)?;
215                }
216            }
217        } else {
218            writeln!(w, "t=0 0\r")?;
219        }
220
221        if !self.time_zones.is_empty() {
222            TimeZone::write(w, &self.time_zones)?;
223        }
224
225        if let Some(ref key) = self.key {
226            key.write(w)?;
227        }
228
229        for attribute in &self.attributes {
230            attribute.write(w)?;
231        }
232
233        for media in &self.medias {
234            media.write(w)?;
235        }
236
237        Ok(())
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244
245    #[test]
246    fn write_sdp() {
247        let sdp = Session {
248            origin: Origin {
249                username: Some("jdoe".into()),
250                sess_id: "2890844526".into(),
251                sess_version: 2890842807,
252                nettype: "IN".into(),
253                addrtype: "IP4".into(),
254                unicast_address: "10.47.16.5".into(),
255            },
256            session_name: "SDP Seminar".into(),
257            session_description: Some("A Seminar on the session description protocol".into()),
258            uri: Some("http://www.example.com/seminars/sdp.pdf".into()),
259            emails: vec!["j.doe@example.com (Jane Doe)".into()],
260            phones: vec!["+1 617 555-6011".into()],
261            connection: Some(Connection {
262                nettype: "IN".into(),
263                addrtype: "IP4".into(),
264                connection_address: "224.2.17.12/127".into(),
265            }),
266            bandwidths: vec![Bandwidth {
267                bwtype: "AS".into(),
268                bandwidth: 128,
269            }],
270            times: vec![Time {
271                start_time: 2873397496,
272                stop_time: 2873404696,
273                repeats: vec![Repeat {
274                    repeat_interval: 604800,
275                    active_duration: 3600,
276                    offsets: vec![0, 90000],
277                }],
278            }],
279            time_zones: vec![
280                TimeZone {
281                    adjustment_time: 2882844526,
282                    offset: -3600,
283                },
284                TimeZone {
285                    adjustment_time: 2898848070,
286                    offset: 0,
287                },
288            ],
289            key: Some(Key {
290                method: "clear".into(),
291                encryption_key: Some("1234".into()),
292            }),
293            attributes: vec![Attribute {
294                attribute: "recvonly".into(),
295                value: None,
296            }],
297            medias: vec![
298                Media {
299                    media: "audio".into(),
300                    port: 49170,
301                    num_ports: None,
302                    proto: "RTP/AVP".into(),
303                    fmt: "0".into(),
304                    media_title: None,
305                    connections: vec![],
306                    bandwidths: vec![],
307                    key: None,
308                    attributes: vec![],
309                },
310                Media {
311                    media: "video".into(),
312                    port: 51372,
313                    num_ports: Some(2),
314                    proto: "RTP/AVP".into(),
315                    fmt: "99".into(),
316                    media_title: None,
317                    connections: vec![],
318                    bandwidths: vec![],
319                    key: None,
320                    attributes: vec![Attribute {
321                        attribute: "rtpmap".into(),
322                        value: Some("99 h263-1998/90000".into()),
323                    }],
324                },
325            ],
326        };
327
328        let mut written = vec![];
329        sdp.write(&mut written).unwrap();
330
331        let expected = "v=0\r
332o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r
333s=SDP Seminar\r
334i=A Seminar on the session description protocol\r
335u=http://www.example.com/seminars/sdp.pdf\r
336e=j.doe@example.com (Jane Doe)\r
337p=+1 617 555-6011\r
338c=IN IP4 224.2.17.12/127\r
339b=AS:128\r
340t=2873397496 2873404696\r
341r=7d 1h 0 25h\r
342z=2882844526 -1h 2898848070 0\r
343k=clear:1234\r
344a=recvonly\r
345m=audio 49170 RTP/AVP 0\r
346m=video 51372/2 RTP/AVP 99\r
347a=rtpmap:99 h263-1998/90000\r
348";
349
350        assert_eq!(String::from_utf8_lossy(&written), expected);
351    }
352}