1 use crate::global_trait::Marshal; 2 use crate::global_trait::Unmarshal; 3 use crate::rtsp_utils; 4 use indexmap::IndexMap; 5 6 #[derive(Debug, Clone, Default)] 7 pub struct RtspRequest { 8 pub method: String, 9 pub url: String, 10 //url = "rtsp://{}:{}/{}", address, port, path 11 pub address: String, 12 pub port: u16, 13 pub path: String, 14 pub version: String, 15 pub headers: IndexMap<String, String>, 16 pub body: Option<String>, 17 } 18 19 impl RtspRequest { get_header(&self, header_name: &String) -> Option<&String>20 pub fn get_header(&self, header_name: &String) -> Option<&String> { 21 self.headers.get(header_name) 22 } 23 } 24 25 impl Unmarshal for RtspRequest { unmarshal(request_data: &str) -> Option<Self>26 fn unmarshal(request_data: &str) -> Option<Self> { 27 let mut rtsp_request = RtspRequest::default(); 28 let header_end_idx = if let Some(idx) = request_data.find("\r\n\r\n") { 29 let data_except_body = &request_data[..idx]; 30 let mut lines = data_except_body.lines(); 31 //parse the first line 32 if let Some(request_first_line) = lines.next() { 33 let mut fields = request_first_line.split_ascii_whitespace(); 34 if let Some(method) = fields.next() { 35 rtsp_request.method = method.to_string(); 36 } 37 if let Some(url) = fields.next() { 38 rtsp_request.url = url.to_string(); 39 40 if let Some(val) = url.strip_prefix("rtsp://") { 41 if let Some(index) = val.find('/') { 42 let path = &url[7 + index + 1..]; 43 rtsp_request.path = String::from(path); 44 let address_with_port = &url[7..7 + index]; 45 46 let (address_val, port_val) = 47 rtsp_utils::scanf!(address_with_port, ':', String, u16); 48 49 if let Some(address) = address_val { 50 rtsp_request.address = address; 51 } 52 if let Some(port) = port_val { 53 rtsp_request.port = port; 54 } 55 56 print!("address_with_port: {address_with_port}"); 57 } 58 } 59 } 60 if let Some(version) = fields.next() { 61 rtsp_request.version = version.to_string(); 62 } 63 } 64 //parse headers 65 for line in lines { 66 if let Some(index) = line.find(": ") { 67 let name = line[..index].to_string(); 68 let value = line[index + 2..].to_string(); 69 rtsp_request.headers.insert(name, value); 70 } 71 } 72 idx + 4 73 } else { 74 return None; 75 }; 76 77 if request_data.len() > header_end_idx { 78 //parse body 79 rtsp_request.body = Some(request_data[header_end_idx..].to_string()); 80 } 81 82 Some(rtsp_request) 83 } 84 } 85 86 impl Marshal for RtspRequest { marshal(&self) -> String87 fn marshal(&self) -> String { 88 let mut request_str = format!("{} {} {}\r\n", self.method, self.url, self.version); 89 for (header_name, header_value) in &self.headers { 90 if header_name != &"Content-Length".to_string() { 91 request_str += &format!("{header_name}: {header_value}\r\n"); 92 } 93 } 94 if let Some(body) = &self.body { 95 request_str += &format!("Content-Length: {}\r\n", body.len()); 96 } 97 request_str += "\r\n"; 98 if let Some(body) = &self.body { 99 request_str += body; 100 } 101 request_str 102 } 103 } 104 105 #[derive(Debug, Clone, Default)] 106 pub struct RtspResponse { 107 pub version: String, 108 pub status_code: u16, 109 pub reason_phrase: String, 110 pub headers: IndexMap<String, String>, 111 pub body: Option<String>, 112 } 113 114 impl Unmarshal for RtspResponse { unmarshal(request_data: &str) -> Option<Self>115 fn unmarshal(request_data: &str) -> Option<Self> { 116 let mut rtsp_response = RtspResponse::default(); 117 let header_end_idx = if let Some(idx) = request_data.find("\r\n\r\n") { 118 let data_except_body = &request_data[..idx]; 119 let mut lines = data_except_body.lines(); 120 //parse the first line 121 if let Some(request_first_line) = lines.next() { 122 let mut fields = request_first_line.split_ascii_whitespace(); 123 124 if let Some(version) = fields.next() { 125 rtsp_response.version = version.to_string(); 126 } 127 if let Some(status) = fields.next() { 128 if let Ok(status) = status.parse::<u16>() { 129 rtsp_response.status_code = status; 130 } 131 } 132 if let Some(reason_phrase) = fields.next() { 133 rtsp_response.reason_phrase = reason_phrase.to_string(); 134 } 135 } 136 //parse headers 137 for line in lines { 138 if let Some(index) = line.find(": ") { 139 let name = line[..index].to_string(); 140 let value = line[index + 2..].to_string(); 141 rtsp_response.headers.insert(name, value); 142 } 143 } 144 idx + 4 145 } else { 146 return None; 147 }; 148 149 if request_data.len() > header_end_idx { 150 //parse body 151 rtsp_response.body = Some(request_data[header_end_idx..].to_string()); 152 } 153 154 Some(rtsp_response) 155 } 156 } 157 158 impl Marshal for RtspResponse { marshal(&self) -> String159 fn marshal(&self) -> String { 160 let mut response_str = format!( 161 "{} {} {}\r\n", 162 self.version, self.status_code, self.reason_phrase 163 ); 164 for (header_name, header_value) in &self.headers { 165 if header_name != &"Content-Length".to_string() { 166 response_str += &format!("{header_name}: {header_value}\r\n"); 167 } 168 } 169 if let Some(body) = &self.body { 170 response_str += &format!("Content-Length: {}\r\n", body.len()); 171 } 172 response_str += "\r\n"; 173 if let Some(body) = &self.body { 174 response_str += body; 175 } 176 response_str 177 } 178 } 179 180 #[cfg(test)] 181 mod tests { 182 183 use crate::{global_trait::Marshal, http::Unmarshal}; 184 185 use super::RtspRequest; 186 187 use indexmap::IndexMap; 188 use std::io::BufRead; 189 #[allow(dead_code)] read_headers(reader: &mut dyn BufRead) -> Option<IndexMap<String, String>>190 fn read_headers(reader: &mut dyn BufRead) -> Option<IndexMap<String, String>> { 191 let mut headers = IndexMap::new(); 192 loop { 193 let mut line = String::new(); 194 match reader.read_line(&mut line) { 195 Ok(0) => break, 196 Ok(_) => { 197 if let Some(index) = line.find(": ") { 198 let name = line[..index].to_string(); 199 let value = line[index + 2..].trim().to_string(); 200 headers.insert(name, value); 201 } 202 } 203 Err(_) => return None, 204 } 205 } 206 Some(headers) 207 } 208 209 // #[test] 210 // fn test_parse_rtsp_request_chatgpt() { 211 // let data1 = "ANNOUNCE rtsp://127.0.0.1:5544/stream RTSP/1.0\r\n\ 212 // Content-Type: application/sdp\r\n\ 213 // CSeq: 2\r\n\ 214 // User-Agent: Lavf58.76.100\r\n\ 215 // Content-Length: 500\r\n\ 216 // \r\n\ 217 // v=0\r\n\ 218 // o=- 0 0 IN IP4 127.0.0.1\r\n\ 219 // s=No Name\r\n\ 220 // c=IN IP4 127.0.0.1\r\n\ 221 // t=0 0\r\n\ 222 // a=tool:libavformat 58.76.100\r\n\ 223 // m=video 0 RTP/AVP 96\r\n\ 224 // b=AS:284\r\n\ 225 // a=rtpmap:96 H264/90000 226 // a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E\r\n\ 227 // a=control:streamid=0\r\n\ 228 // m=audio 0 RTP/AVP 97\r\n\ 229 // b=AS:128\r\n\ 230 // a=rtpmap:97 MPEG4-GENERIC/48000/2\r\n\ 231 // a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500\r\n\ 232 // a=control:streamid=1\r\n"; 233 234 // // v=0:SDP版本号,通常为0。 235 // // o=- 0 0 IN IP4 127.0.0.1:会话的所有者和会话ID,以及会话开始时间和会话结束时间的信息。 236 // // s=No Name:会话名称或标题。 237 // // c=IN IP4 127.0.0.1:表示会话数据传输的地址类型(IPv4)和地址(127.0.0.1)。 238 // // t=0 0:会话时间,包括会话开始时间和结束时间,这里的值都是0,表示会话没有预定义的结束时间。 239 // // a=tool:libavformat 58.76.100:会话所使用的工具或软件名称和版本号。 240 241 // // m=video 0 RTP/AVP 96:媒体类型(video或audio)、媒体格式(RTP/AVP)、媒体格式编号(96)和媒体流的传输地址。 242 // // b=AS:284:视频流所使用的带宽大小。 243 // // a=rtpmap:96 H264/90000:视频流所使用的编码方式(H.264)和时钟频率(90000)。 244 // // a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E:视频流的格式参数,如分片方式、SPS和PPS等。 245 // // a=control:streamid=0:指定视频流的流ID。 246 247 // // m=audio 0 RTP/AVP 97:媒体类型(audio)、媒体格式(RTP/AVP)、媒体格式编号(97)和媒体流的传输地址。 248 // // b=AS:128:音频流所使用的带宽大小。 249 // // a=rtpmap:97 MPEG4-GENERIC/48000/2:音频流所使用的编码方式(MPEG4-GENERIC)、采样率(48000Hz)、和通道数(2)。 250 // // a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500:音频流的格式参数,如编码方式、采样长度、索引长度等。 251 // // a=control:streamid=1:指定音频流的流ID。 252 253 // if let Some(request) = parse_request_bytes(&data1.as_bytes()) { 254 // println!(" parser: {:?}", request); 255 // } 256 // } 257 258 #[test] test_parse_rtsp_request()259 fn test_parse_rtsp_request() { 260 let data1 = "SETUP rtsp://127.0.0.1/stream/streamid=0 RTSP/1.0\r\n\ 261 Transport: RTP/AVP/TCP;unicast;interleaved=0-1;mode=record\r\n\ 262 CSeq: 3\r\n\ 263 User-Agent: Lavf58.76.100\r\n\ 264 \r\n"; 265 266 if let Some(parser) = RtspRequest::unmarshal(data1) { 267 println!(" parser: {parser:?}"); 268 let marshal_result = parser.marshal(); 269 print!("marshal result: =={marshal_result}=="); 270 assert_eq!(data1, marshal_result); 271 } 272 273 let data2 = "ANNOUNCE rtsp://127.0.0.1/stream RTSP/1.0\r\n\ 274 Content-Type: application/sdp\r\n\ 275 CSeq: 2\r\n\ 276 User-Agent: Lavf58.76.100\r\n\ 277 Content-Length: 500\r\n\ 278 \r\n\ 279 v=0\r\n\ 280 o=- 0 0 IN IP4 127.0.0.1\r\n\ 281 s=No Name\r\n\ 282 c=IN IP4 127.0.0.1\r\n\ 283 t=0 0\r\n\ 284 a=tool:libavformat 58.76.100\r\n\ 285 m=video 0 RTP/AVP 96\r\n\ 286 b=AS:284\r\n\ 287 a=rtpmap:96 H264/90000\r\n\ 288 a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E\r\n\ 289 a=control:streamid=0\r\n\ 290 m=audio 0 RTP/AVP 97\r\n\ 291 b=AS:128\r\n\ 292 a=rtpmap:97 MPEG4-GENERIC/48000/2\r\n\ 293 a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500\r\n\ 294 a=control:streamid=1\r\n"; 295 296 if let Some(parser) = RtspRequest::unmarshal(data2) { 297 println!(" parser: {parser:?}"); 298 let marshal_result = parser.marshal(); 299 print!("marshal result: =={marshal_result}=="); 300 assert_eq!(data2, marshal_result); 301 } 302 } 303 304 #[test] test_http_status_code()305 fn test_http_status_code() { 306 let stats_code = http::StatusCode::OK; 307 308 println!( 309 "stats_code: {}, {}", 310 stats_code.canonical_reason().unwrap(), 311 stats_code.as_u16() 312 ) 313 } 314 } 315