sdp_rs/
media_description.rs

1use crate::{lines, Error};
2use std::convert::{TryFrom, TryInto};
3
4/// The Media description high level type tokenizer. It tokenizes all lines related to a Media
5/// description.
6/// This is low level stuff and you shouldn't interact directly
7/// with it, unless you know what you are doing.
8pub use crate::tokenizers::media_description::Tokenizer;
9
10/// The Media description high level type. This type holds all types related to a complete Media
11/// description (info, connections, bandwidths, attributes etc).
12#[derive(Debug, PartialEq, PartialOrd, Clone)]
13pub struct MediaDescription {
14    pub media: lines::Media,
15    pub info: Option<lines::SessionInformation>,
16    pub connections: Vec<lines::Connection>,
17    pub bandwidths: Vec<lines::Bandwidth>,
18    pub key: Option<lines::Key>,
19    pub attributes: Vec<lines::Attribute>,
20}
21
22impl<'a> TryFrom<Tokenizer<'a>> for MediaDescription {
23    type Error = Error;
24
25    fn try_from(tokenizer: Tokenizer<'a>) -> Result<Self, Self::Error> {
26        Ok(Self {
27            media: tokenizer.media.try_into()?,
28            info: tokenizer.info.map(Into::into),
29            connections: tokenizer
30                .connections
31                .into_iter()
32                .map(TryInto::try_into)
33                .collect::<Result<Vec<_>, _>>()?,
34            bandwidths: tokenizer
35                .bandwidths
36                .into_iter()
37                .map(TryInto::try_into)
38                .collect::<Result<Vec<_>, _>>()?,
39            key: tokenizer.key.map(Into::into),
40            attributes: tokenizer
41                .attributes
42                .into_iter()
43                .map(TryInto::try_into)
44                .collect::<Result<Vec<_>, _>>()?,
45        })
46    }
47}
48
49impl std::fmt::Display for MediaDescription {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        write!(f, "m={}", self.media)?;
52        if let Some(info) = &self.info {
53            write!(f, "\r\n{}", info)?
54        }
55        for connection in self.connections.iter() {
56            write!(f, "\r\n{}", connection)?
57        }
58        for bandwidth in self.bandwidths.iter() {
59            write!(f, "\r\n{}", bandwidth)?
60        }
61        if let Some(key) = &self.key {
62            write!(f, "\r\n{}", key)?
63        }
64        for attribute in self.attributes.iter() {
65            write!(f, "\r\n{}", attribute)?
66        }
67
68        Ok(())
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use std::net::IpAddr;
76
77    #[test]
78    fn from_tokenizer1() {
79        let tokenizer = Tokenizer {
80            media: lines::media::Tokenizer {
81                media: "audio",
82                port: "49170".into(),
83                proto: "RTP/AVP",
84                fmt: "0",
85            },
86            info: Some("audio media".into()),
87            connections: vec![
88                lines::connection::Tokenizer {
89                    nettype: "IN",
90                    addrtype: "IP4",
91                    connection_address: "10.47.16.5".into(),
92                },
93                lines::connection::Tokenizer {
94                    nettype: "IN",
95                    addrtype: "IP4",
96                    connection_address: "10.47.16.6".into(),
97                },
98            ],
99            bandwidths: vec![("CT", "1000").into(), ("AS", "551").into()],
100            key: Some("prompt".into()),
101            attributes: vec![
102                ("rtpmap", "99 h232-199/90000").into(),
103                ("rtpmap", "90 h263-1998/90000").into(),
104            ],
105        };
106
107        assert_eq!(
108            MediaDescription::try_from(tokenizer),
109            Ok(MediaDescription {
110                media: lines::Media {
111                    media: lines::media::MediaType::Audio,
112                    port: 49170,
113                    num_of_ports: None,
114                    proto: lines::media::ProtoType::RtpAvp,
115                    fmt: "0".into()
116                },
117                info: Some(lines::SessionInformation::new("audio media".into())),
118                connections: vec![
119                    lines::Connection {
120                        nettype: lines::common::Nettype::In,
121                        addrtype: lines::common::Addrtype::Ip4,
122                        connection_address: "10.47.16.5".parse::<IpAddr>().unwrap().into()
123                    },
124                    lines::Connection {
125                        nettype: lines::common::Nettype::In,
126                        addrtype: lines::common::Addrtype::Ip4,
127                        connection_address: "10.47.16.6".parse::<IpAddr>().unwrap().into()
128                    },
129                ],
130                bandwidths: vec![
131                    lines::Bandwidth {
132                        bwtype: lines::bandwidth::Bwtype::Ct,
133                        bandwidth: 1000
134                    },
135                    lines::Bandwidth {
136                        bwtype: lines::bandwidth::Bwtype::As,
137                        bandwidth: 551
138                    }
139                ],
140                key: Some(lines::Key {
141                    method: lines::key::KeyMethod::Prompt,
142                    encryption_key: Default::default()
143                }),
144                attributes: vec![
145                    lines::Attribute::Rtpmap(lines::attribute::Rtpmap {
146                        payload_type: 99,
147                        encoding_name: "h232-199".into(),
148                        clock_rate: 90000,
149                        encoding_params: None
150                    }),
151                    lines::Attribute::Rtpmap(lines::attribute::Rtpmap {
152                        payload_type: 90,
153                        encoding_name: "h263-1998".into(),
154                        clock_rate: 90000,
155                        encoding_params: None
156                    }),
157                ]
158            })
159        );
160    }
161
162    #[test]
163    fn display1() {
164        let media_description = MediaDescription {
165            media: lines::Media {
166                media: lines::media::MediaType::Audio,
167                port: 49170,
168                num_of_ports: None,
169                proto: lines::media::ProtoType::RtpAvp,
170                fmt: "0".into(),
171            },
172            info: Some(lines::SessionInformation::new("audio media".into())),
173            connections: vec![
174                lines::Connection {
175                    nettype: lines::common::Nettype::In,
176                    addrtype: lines::common::Addrtype::Ip4,
177                    connection_address: "10.47.16.5".parse::<IpAddr>().unwrap().into(),
178                },
179                lines::Connection {
180                    nettype: lines::common::Nettype::In,
181                    addrtype: lines::common::Addrtype::Ip4,
182                    connection_address: "10.47.16.6".parse::<IpAddr>().unwrap().into(),
183                },
184            ],
185            bandwidths: vec![
186                lines::Bandwidth {
187                    bwtype: lines::bandwidth::Bwtype::Ct,
188                    bandwidth: 1000,
189                },
190                lines::Bandwidth {
191                    bwtype: lines::bandwidth::Bwtype::As,
192                    bandwidth: 551,
193                },
194            ],
195            key: Some(lines::Key {
196                method: lines::key::KeyMethod::Prompt,
197                encryption_key: Default::default(),
198            }),
199            attributes: vec![
200                lines::Attribute::Rtpmap(lines::attribute::Rtpmap {
201                    payload_type: 99,
202                    encoding_name: "h232-199".into(),
203                    clock_rate: 90000,
204                    encoding_params: None,
205                }),
206                lines::Attribute::Rtpmap(lines::attribute::Rtpmap {
207                    payload_type: 90,
208                    encoding_name: "h263-1998".into(),
209                    clock_rate: 90000,
210                    encoding_params: None,
211                }),
212            ],
213        };
214
215        assert_eq!(
216            media_description.to_string(),
217            concat!(
218                "m=audio 49170 RTP/AVP 0\r\n",
219                "i=audio media\r\n",
220                "c=IN IP4 10.47.16.5\r\n",
221                "c=IN IP4 10.47.16.6\r\n",
222                "b=CT:1000\r\n",
223                "b=AS:551\r\n",
224                "k=prompt\r\n",
225                "a=rtpmap:99 h232-199/90000\r\n",
226                "a=rtpmap:90 h263-1998/90000"
227            )
228        );
229    }
230
231    #[test]
232    fn display2() {
233        let media_description = MediaDescription {
234            media: lines::Media {
235                media: lines::media::MediaType::Audio,
236                port: 49170,
237                num_of_ports: None,
238                proto: lines::media::ProtoType::RtpAvp,
239                fmt: "0".into(),
240            },
241            info: None,
242            connections: vec![],
243            bandwidths: vec![],
244            key: None,
245            attributes: vec![],
246        };
247
248        assert_eq!(
249            media_description.to_string(),
250            concat!("m=audio 49170 RTP/AVP 0",)
251        );
252    }
253}