xref: /xiu/protocol/rtsp/src/sdp/mod.rs (revision b36cf5da)
1 pub mod fmtp;
2 pub mod rtpmap;
3 
4 use crate::global_trait::{Marshal, Unmarshal};
5 use rtpmap::RtpMap;
6 use std::collections::HashMap;
7 
8 use self::fmtp::Fmtp;
9 
10 #[derive(Debug, Clone, Default)]
11 pub struct Bandwidth {
12     b_type: String,
13     bandwidth: u16,
14 }
15 
16 impl Unmarshal for Bandwidth {
17     //   b=AS:284\r\n\
unmarshal(raw_data: &str) -> Option<Self>18     fn unmarshal(raw_data: &str) -> Option<Self> {
19         let mut sdp_bandwidth = Bandwidth::default();
20 
21         let parameters: Vec<&str> = raw_data.split(':').collect();
22         if let Some(t) = parameters.first() {
23             sdp_bandwidth.b_type = t.to_string();
24         }
25 
26         if let Some(bandwidth) = parameters.get(1) {
27             if let Ok(bandwidth) = bandwidth.parse::<u16>() {
28                 sdp_bandwidth.bandwidth = bandwidth;
29             }
30         }
31 
32         Some(sdp_bandwidth)
33     }
34 }
35 
36 impl Marshal for Bandwidth {
marshal(&self) -> String37     fn marshal(&self) -> String {
38         format!("{}:{}\r\n", self.b_type, self.bandwidth)
39     }
40 }
41 
42 /*
43 v=0
44 o=- 946685052188730 1 IN IP4 0.0.0.0
45 s=RTSP/RTP Server
46 i=playback/robot=040082d087c335e3bd2b/camera=head/timerang1=1533620879-1533620898
47 t=0 0
48 a=tool:vlc 0.9.8a
49 a=type:broadcast
50 a=control:*
51 a=range:npt=0-
52 m=video 20003 RTP/AVP 97
53 b=RR:0
54 a=rtpmap:97 H264/90000
55 a=fmtp:97 profile-level-id=42C01E;packetization-mode=1;sprop-parameter-sets=Z0LAHtkDxWhAAAADAEAAAAwDxYuSAAAAAQ==,aMuMsgAAAAE=
56 a=control:track1
57 m=audio 11704 RTP/AVP 96 97 98 0 8 18 101 99 100 */
58 
59 #[derive(Default, Debug, Clone)]
60 pub struct SdpMediaInfo {
61     pub media_type: String,
62     port: usize,
63     protocol: String,
64     fmts: Vec<u8>,
65     bandwidth: Option<Bandwidth>,
66     pub rtpmap: RtpMap,
67     pub fmtp: Option<fmtp::Fmtp>,
68     pub attributes: HashMap<String, String>,
69 }
70 
71 // impl std::fmt::Debug for dyn TMsgConverter {
72 //     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
73 //         write!(fmt, "S2 {{ member: {:?} }}", self.member)
74 //     }
75 // }
76 
77 // impl Default for SdpMediaInfo {
78 //     fn default() -> Self {
79 //         Self {
80 //             fmtp: Box::new(fmtp::UnknownFmtpSdp::default()),
81 //             ..Default::default()
82 //         }
83 //     }
84 // }
85 
86 #[derive(Default, Debug, Clone)]
87 pub struct Sdp {
88     pub raw_string: String,
89     version: u16,
90     origin: String,
91     session: String,
92     connection: String,
93     timing: String,
94     pub medias: Vec<SdpMediaInfo>,
95     attributes: HashMap<String, String>,
96 }
97 
98 impl Unmarshal for SdpMediaInfo {
99     //m=audio 11704 RTP/AVP 96 97 98 0 8 18 101 99 100 */
100     //m=video 20003 RTP/AVP 97
unmarshal(raw_data: &str) -> Option<Self>101     fn unmarshal(raw_data: &str) -> Option<Self> {
102         let mut sdp_media = SdpMediaInfo::default();
103         let parameters: Vec<&str> = raw_data.split(' ').collect();
104 
105         if let Some(para_0) = parameters.first() {
106             sdp_media.media_type = para_0.to_string();
107         }
108 
109         if let Some(para_1) = parameters.get(1) {
110             if let Ok(port) = para_1.parse::<usize>() {
111                 sdp_media.port = port;
112             }
113         }
114 
115         if let Some(para_2) = parameters.get(2) {
116             sdp_media.protocol = para_2.to_string();
117         }
118 
119         let mut cur_param_idx = 3;
120 
121         while let Some(fmt_str) = parameters.get(cur_param_idx) {
122             if let Ok(fmt) = fmt_str.parse::<u8>() {
123                 sdp_media.fmts.push(fmt);
124             }
125             cur_param_idx += 1;
126         }
127 
128         Some(sdp_media)
129     }
130 }
131 
132 // m=video 0 RTP/AVP 96\r\n\
133 // b=AS:284\r\n\
134 // a=rtpmap:96 H264/90000\r\n\
135 // a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E\r\n\
136 // a=control:streamid=0\r\n\
137 // m=audio 0 RTP/AVP 97\r\n\
138 // b=AS:128\r\n\
139 // a=rtpmap:97 MPEG4-GENERIC/48000/2\r\n\
140 // a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500\r\n\
141 // a=control:streamid=1\r\n"
142 
143 impl Marshal for SdpMediaInfo {
marshal(&self) -> String144     fn marshal(&self) -> String {
145         let fmts_str = self
146             .fmts
147             .iter()
148             .map(|b| b.to_string())
149             .collect::<Vec<String>>()
150             .join(" ");
151 
152         let bandwidth = if let Some(bandwidth) = &self.bandwidth {
153             format!("b={}", bandwidth.marshal())
154         } else {
155             String::from("")
156         };
157 
158         let mut sdp_media_info = format!(
159             "m={} {} {} {}\r\n{}a=rtpmap:{}",
160             self.media_type,
161             self.port,
162             self.protocol,
163             fmts_str,
164             bandwidth,
165             self.rtpmap.marshal()
166         );
167 
168         if let Some(fmtp) = &self.fmtp {
169             sdp_media_info = format!("{}a=fmtp:{}", sdp_media_info, fmtp.marshal());
170         }
171 
172         for (k, v) in &self.attributes {
173             sdp_media_info = format!("{sdp_media_info}a={k}:{v}\r\n");
174         }
175 
176         sdp_media_info
177     }
178 }
179 
180 impl Unmarshal for Sdp {
unmarshal(raw_data: &str) -> Option<Self>181     fn unmarshal(raw_data: &str) -> Option<Self> {
182         let mut sdp = Sdp {
183             raw_string: raw_data.to_string(),
184             ..Default::default()
185         };
186 
187         let lines: Vec<&str> = raw_data.split(|c| c == '\r' || c == '\n').collect();
188         for line in lines {
189             if line.is_empty() {
190                 continue;
191             }
192             let kv: Vec<&str> = line.trim().splitn(2, '=').collect();
193             if kv.len() < 2 {
194                 log::error!("Sdp current line : {} parse error!", line);
195                 continue;
196             }
197 
198             match kv[0] {
199                 //m=audio 11704 RTP/AVP 96 97 98 0 8 18 101 99 100 */
200                 //m=video 20003 RTP/AVP 97
201 
202                 // v=0\r\n\
203                 // o=- 0 0 IN IP4 127.0.0.1\r\n\
204                 // s=No Name\r\n\
205                 // c=IN IP4 127.0.0.1\r\n\
206                 // t=0 0\r\n\
207 
208                 // m=video 0 RTP/AVP 96\r\n\
209                 // b=AS:284\r\n\
210                 // a=rtpmap:96 H264/90000\r\n\
211                 // a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E\r\n\
212                 // a=control:streamid=0\r\n\
213                 // m=audio 0 RTP/AVP 97\r\n\
214                 // b=AS:128\r\n\
215                 // a=rtpmap:97 MPEG4-GENERIC/48000/2\r\n\
216                 // a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500\r\n\
217                 // a=control:streamid=1\r\n";
218                 "v" => {
219                     if let Ok(version) = kv[1].parse::<u16>() {
220                         sdp.version = version;
221                     }
222                 }
223                 "o" => {
224                     sdp.origin = kv[1].to_string();
225                 }
226                 "s" => {
227                     sdp.session = kv[1].to_string();
228                 }
229                 "c" => {
230                     sdp.connection = kv[1].to_string();
231                 }
232                 "t" => {
233                     sdp.timing = kv[1].to_string();
234                 }
235                 "m" => {
236                     if let Some(sdp_media) = SdpMediaInfo::unmarshal(kv[1]) {
237                         sdp.medias.push(sdp_media);
238                     }
239                 }
240                 "b" => {
241                     if let Some(cur_media) = sdp.medias.last_mut() {
242                         cur_media.bandwidth = Some(Bandwidth::unmarshal(kv[1]).unwrap());
243                     } else {
244                         continue;
245                     }
246                 }
247                 // a=rtpmap:96 H264/90000\r\n\
248                 // a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E\r\n\
249                 // a=control:streamid=0\r\n\
250                 "a" => {
251                     let attribute: Vec<&str> = kv[1].splitn(2, ':').collect();
252 
253                     let attr_name = attribute[0];
254                     let attr_value = if let Some(val) = attribute.get(1) {
255                         val
256                     } else {
257                         ""
258                     };
259 
260                     if let Some(cur_media) = sdp.medias.last_mut() {
261                         if attribute.len() == 2 {
262                             match attr_name {
263                                 "rtpmap" => {
264                                     if let Some(rtpmap) = RtpMap::unmarshal(attr_value) {
265                                         cur_media.rtpmap = rtpmap;
266                                         continue;
267                                     }
268                                 }
269                                 "fmtp" => {
270                                     cur_media.fmtp =
271                                         Fmtp::new(&cur_media.rtpmap.encoding_name, attr_value);
272                                     continue;
273                                 }
274                                 _ => {}
275                             }
276                         }
277                         cur_media
278                             .attributes
279                             .insert(attr_name.to_string(), attr_value.to_string());
280                     } else {
281                         sdp.attributes
282                             .insert(attr_name.to_string(), attr_value.to_string());
283                     }
284                 }
285 
286                 _ => {
287                     log::info!("not parsed: {}", line);
288                 }
289             }
290         }
291 
292         Some(sdp)
293     }
294 }
295 
296 // v=0\r\n\
297 // o=- 0 0 IN IP4 127.0.0.1\r\n\
298 // s=No Name\r\n\
299 // c=IN IP4 127.0.0.1\r\n\
300 // t=0 0\r\n\
301 // a=tool:libavformat 58.76.100\r\n\
302 
303 impl Marshal for Sdp {
marshal(&self) -> String304     fn marshal(&self) -> String {
305         let mut sdp_str = format!(
306             "v={}\r\no={}\r\ns={}\r\nc={}\r\nt={}\r\n",
307             self.version, self.origin, self.session, self.connection, self.timing
308         );
309 
310         for (k, v) in &self.attributes {
311             sdp_str = format!("{sdp_str}a={k}:{v}\r\n");
312         }
313 
314         for media_info in &self.medias {
315             sdp_str = format!("{}{}", sdp_str, media_info.marshal());
316         }
317 
318         sdp_str
319     }
320 }
321 
322 #[cfg(test)]
323 mod tests {
324 
325     use crate::global_trait::{Marshal, Unmarshal};
326 
327     use super::Sdp;
328 
329     #[test]
test_parse_sdp()330     fn test_parse_sdp() {
331         let data2 = "ANNOUNCE rtsp://127.0.0.1:5544/stream RTSP/1.0\r\n\
332         Content-Type: application/sdp\r\n\
333         CSeq: 2\r\n\
334         User-Agent: Lavf58.76.100\r\n\
335         Content-Length: 500\r\n\
336         \r\n\
337         v=0\r\n\
338         o=- 0 0 IN IP4 127.0.0.1\r\n\
339         s=No Name\r\n\
340         c=IN IP4 127.0.0.1\r\n\
341         t=0 0\r\n\
342         a=tool:libavformat 58.76.100\r\n\
343         m=video 0 RTP/AVP 96\r\n\
344         b=AS:284\r\n\
345         a=rtpmap:96 H264/90000\r\n\
346         a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E\r\n\
347         a=control:streamid=0\r\n\
348         m=audio 0 RTP/AVP 97\r\n\
349         b=AS:128\r\n\
350         a=rtpmap:97 MPEG4-GENERIC/48000/2\r\n\
351         a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500\r\n\
352         a=control:streamid=1\r\n";
353 
354         // v=0:SDP版本号,通常为0。
355         // o=- 0 0 IN IP4 127.0.0.1:会话的所有者和会话ID,以及会话开始时间和会话结束时间的信息。
356         // s=No Name:会话名称或标题。
357         // c=IN IP4 127.0.0.1:表示会话数据传输的地址类型(IPv4)和地址(127.0.0.1)。
358         // t=0 0:会话时间,包括会话开始时间和结束时间,这里的值都是0,表示会话没有预定义的结束时间。
359         // a=tool:libavformat 58.76.100:会话所使用的工具或软件名称和版本号。
360 
361         // m=video 0 RTP/AVP 96:媒体类型(video或audio)、媒体格式(RTP/AVP)、媒体格式编号(96)和媒体流的传输地址。
362         // b=AS:284:视频流所使用的带宽大小。
363         // a=rtpmap:96 H264/90000:视频流所使用的编码方式(H.264)和时钟频率(90000)。
364         // a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E:视频流的格式参数,如分片方式、SPS和PPS等。
365         // a=control:streamid=0:指定视频流的流ID。
366 
367         // m=audio 0 RTP/AVP 97:媒体类型(audio)、媒体格式(RTP/AVP)、媒体格式编号(97)和媒体流的传输地址。
368         // b=AS:128:音频流所使用的带宽大小。
369         // a=rtpmap:97 MPEG4-GENERIC/48000/2:音频流所使用的编码方式(MPEG4-GENERIC)、采样率(48000Hz)、和通道数(2)。
370         // a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500:音频流的格式参数,如编码方式、采样长度、索引长度等。
371         // a=control:streamid=1:指定音频流的流ID。
372 
373         if let Some(sdp) = Sdp::unmarshal(data2) {
374             println!("sdp : {sdp:?}");
375 
376             println!("sdp str : {}", sdp.marshal());
377         }
378     }
379     #[test]
test_str()380     fn test_str() {
381         let fmts: Vec<u8> = vec![5];
382         // fmts.push(6);
383         let fmts_str = fmts
384             .iter()
385             .map(|b| b.to_string())
386             .collect::<Vec<String>>()
387             .join(" ");
388 
389         println!("=={fmts_str}==");
390     }
391 }
392