1use crate::{lines, Error};
2use std::convert::{TryFrom, TryInto};
3
4pub use crate::tokenizers::media_description::Tokenizer;
9
10#[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}