github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/media/serverRTSP.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package media 20 21 import ( 22 "bytes" 23 "errors" 24 "fmt" 25 "net" 26 "net/url" 27 "strconv" 28 "strings" 29 "time" 30 ) 31 32 var ( 33 Version = "RTSP/1.0" 34 UserAgent = "Lavf58.29.100" 35 Session = "000a959d6816" 36 ) 37 var ( 38 OPTIONS = "OPTIONS" 39 DESCRIBE = "DESCRIBE" 40 SETUP = "SETUP" 41 PLAY = "PLAY" 42 TEARDOWN = "TEARDOWN" 43 ) 44 45 // RTSP response status codes 46 const ( 47 StatusContinue = 100 48 StatusOK = 200 49 StatusCreated = 201 50 StatusLowOnStorageSpace = 250 51 StatusMultipleChoices = 300 52 StatusMovedPermanently = 301 53 StatusMovedTemporarily = 302 54 StatusSeeOther = 303 55 StatusNotModified = 304 56 StatusUseProxy = 305 57 StatusBadRequest = 400 58 StatusUnauthorized = 401 59 StatusPaymentRequired = 402 60 StatusForbidden = 403 61 StatusNotFound = 404 62 StatusMethodNotAllowed = 405 63 StatusNotAcceptable = 406 64 StatusProxyAuthenticationRequired = 407 65 StatusRequestTimeout = 408 66 StatusGone = 410 67 StatusLengthRequired = 411 68 StatusPreconditionFailed = 412 69 StatusRequestEntityTooLarge = 413 70 StatusRequestURITooLong = 414 71 StatusUnsupportedMediaType = 415 72 StatusInvalidparameter = 451 73 StatusIllegalConferenceIdentifier = 452 74 StatusNotEnoughBandwidth = 453 75 StatusSessionNotFound = 454 76 StatusMethodNotValidInThisState = 455 77 StatusHeaderFieldNotValid = 456 78 StatusInvalidRange = 457 79 StatusParameterIsReadOnly = 458 80 StatusAggregateOperationNotAllowed = 459 81 StatusOnlyAggregateOperationAllowed = 460 82 StatusUnsupportedTransport = 461 83 StatusDestinationUnreachable = 462 84 StatusInternalServerError = 500 85 StatusNotImplemented = 501 86 StatusBadGateway = 502 87 StatusServiceUnavailable = 503 88 StatusGatewayTimeout = 504 89 StatusRTSPVersionNotSupported = 505 90 StatusOptionNotsupport = 551 91 ) 92 93 func StatusText(code int) string { 94 return statusText[code] 95 } 96 97 var statusText = map[int]string{ 98 StatusContinue: "Continue", 99 StatusOK: "OK", 100 StatusCreated: "Created", 101 StatusLowOnStorageSpace: "Low on Storage Space", 102 StatusMultipleChoices: "Multiple Choices", 103 StatusMovedPermanently: "Moved Permanently", 104 StatusMovedTemporarily: "Moved Temporarily", 105 StatusSeeOther: "See Other", 106 StatusNotModified: "Not Modified", 107 StatusUseProxy: "Use Proxy", 108 StatusBadRequest: "Bad Request", 109 StatusUnauthorized: "Unauthorized", 110 StatusPaymentRequired: "Payment Required", 111 StatusForbidden: "Forbidden", 112 StatusNotFound: "Not Found", 113 StatusMethodNotAllowed: "Method Not Allowed", 114 StatusNotAcceptable: "Not Acceptable", 115 StatusProxyAuthenticationRequired: "Proxy Authentication Required", 116 StatusRequestTimeout: "Request Time-out", 117 StatusGone: "Gone", 118 StatusLengthRequired: "Length Required", 119 StatusPreconditionFailed: "Precondition Failed", 120 StatusRequestEntityTooLarge: "Request Entity Too Large", 121 StatusRequestURITooLong: "Request-URI Too Large", 122 StatusUnsupportedMediaType: "Unsupported Media Type", 123 StatusInvalidparameter: "Parameter Not Understood", 124 StatusIllegalConferenceIdentifier: "Conference Not Found", 125 StatusNotEnoughBandwidth: "Not Enough Bandwidth", 126 StatusSessionNotFound: "Session Not Found", 127 StatusMethodNotValidInThisState: "Method Not Valid in This State", 128 StatusHeaderFieldNotValid: "Header Field Not Valid for Resource", 129 StatusInvalidRange: "Invalid Range", 130 StatusParameterIsReadOnly: "Parameter Is Read-Only", 131 StatusAggregateOperationNotAllowed: "Aggregate operation not allowed", 132 StatusOnlyAggregateOperationAllowed: "Only aggregate operation allowed", 133 StatusUnsupportedTransport: "Unsupported transport", 134 StatusDestinationUnreachable: "Destination unreachable", 135 StatusInternalServerError: "Internal Server Error", 136 StatusNotImplemented: "Not Implemented", 137 StatusBadGateway: "Bad Gateway", 138 StatusServiceUnavailable: "Service Unavailable", 139 StatusGatewayTimeout: "Gateway Time-out", 140 StatusRTSPVersionNotSupported: "RTSP Version not supported", 141 StatusOptionNotsupport: "Option not supported", 142 } 143 144 // RTSPServer func 145 func RTSPServer() { 146 log.Info("Server RTSP start") 147 l, err := net.Listen("tcp", Storage.ServerRTSPPort()) 148 if err != nil { 149 log.Error(err.Error()) 150 return 151 } 152 defer func() { 153 err := l.Close() 154 if err != nil { 155 log.Error(err.Error()) 156 } 157 }() 158 for { 159 conn, err := l.Accept() 160 if err != nil { 161 log.Error(err.Error()) 162 return 163 } 164 go RTSPServerClientHandle(conn) 165 } 166 } 167 168 // RTSPServerClientHandle func 169 func RTSPServerClientHandle(conn net.Conn) { 170 buf := make([]byte, 4096) 171 token, uuid, channel, in, cSEQ := "", "", "0", 0, 0 172 var playStarted bool 173 defer func() { 174 err := conn.Close() 175 if err != nil { 176 log.Error(err.Error()) 177 } 178 179 }() 180 err := conn.SetDeadline(time.Now().Add(10 * time.Second)) 181 if err != nil { 182 log.Error(err.Error()) 183 return 184 } 185 for { 186 n, err := conn.Read(buf) 187 if err != nil { 188 log.Error(err.Error()) 189 return 190 } 191 cSEQ = parsecSEQ(buf[:n]) 192 stage, err := parseStage(buf[:n]) 193 if err != nil { 194 log.Error(err.Error()) 195 } 196 err = conn.SetDeadline(time.Now().Add(60 * time.Second)) 197 log.Debug(string(buf[:n])) 198 199 if err != nil { 200 log.Error(err.Error()) 201 return 202 } 203 204 switch stage { 205 case OPTIONS: 206 if playStarted { 207 err = RTSPServerClientResponse(uuid, channel, conn, 200, map[string]string{"CSeq": strconv.Itoa(cSEQ), "Public": "DESCRIBE, SETUP, TEARDOWN, PLAY"}) 208 if err != nil { 209 return 210 } 211 continue 212 } 213 uuid, channel, token, err = parseStreamChannel(buf[:n]) 214 if err != nil { 215 log.Error(err.Error()) 216 return 217 } 218 if !Storage.StreamChannelExist(uuid, channel) { 219 log.Error(ErrorStreamNotFound.Error()) 220 err = RTSPServerClientResponse(uuid, channel, conn, 404, map[string]string{"CSeq": strconv.Itoa(cSEQ)}) 221 if err != nil { 222 return 223 } 224 return 225 } 226 227 if !RemoteAuthorization("RTSP", uuid, channel, token, conn.RemoteAddr().String()) { 228 log.Error(ErrorStreamUnauthorized.Error()) 229 err = RTSPServerClientResponse(uuid, channel, conn, 401, map[string]string{"CSeq": strconv.Itoa(cSEQ)}) 230 if err != nil { 231 return 232 } 233 return 234 } 235 236 Storage.StreamChannelRun(uuid, channel) 237 err = RTSPServerClientResponse(uuid, channel, conn, 200, map[string]string{"CSeq": strconv.Itoa(cSEQ), "Public": "DESCRIBE, SETUP, TEARDOWN, PLAY"}) 238 if err != nil { 239 return 240 } 241 case SETUP: 242 if !strings.Contains(string(buf[:n]), "interleaved") { 243 err = RTSPServerClientResponse(uuid, channel, conn, 461, map[string]string{"CSeq": strconv.Itoa(cSEQ)}) 244 if err != nil { 245 return 246 } 247 continue 248 } 249 err = RTSPServerClientResponse(uuid, channel, conn, 200, map[string]string{"CSeq": strconv.Itoa(cSEQ), "User-Agent:": UserAgent, "Session": Session, "Transport": "RTP/AVP/TCP;unicast;interleaved=" + strconv.Itoa(in) + "-" + strconv.Itoa(in+1)}) 250 if err != nil { 251 return 252 } 253 in = in + 2 254 case DESCRIBE: 255 sdp, err := Storage.StreamChannelSDP(uuid, channel) 256 if err != nil { 257 log.Error(err.Error()) 258 return 259 } 260 err = RTSPServerClientResponse(uuid, channel, conn, 200, map[string]string{"CSeq": strconv.Itoa(cSEQ), "User-Agent:": UserAgent, "Session": Session, "Content-Type": "application/sdp\r\nContent-Length: " + strconv.Itoa(len(sdp)), "sdp": string(sdp)}) 261 if err != nil { 262 return 263 } 264 case PLAY: 265 err = RTSPServerClientResponse(uuid, channel, conn, 200, map[string]string{"CSeq": strconv.Itoa(cSEQ), "User-Agent:": UserAgent, "Session": Session}) 266 if err != nil { 267 return 268 } 269 playStarted = true 270 go RTSPServerClientPlay(uuid, channel, conn) 271 case TEARDOWN: 272 err = RTSPServerClientResponse(uuid, channel, conn, 200, map[string]string{"CSeq": strconv.Itoa(cSEQ), "User-Agent:": UserAgent, "Session": Session}) 273 if err != nil { 274 return 275 } 276 return 277 default: 278 log.Debugf("stage bad %v", stage) 279 } 280 } 281 } 282 283 // handleRTSPServerPlay func 284 func RTSPServerClientPlay(uuid string, channel string, conn net.Conn) { 285 cid, _, ch, err := Storage.ClientAdd(uuid, channel, RTSP) 286 if err != nil { 287 log.Error(err.Error()) 288 return 289 } 290 defer func() { 291 Storage.ClientDelete(uuid, cid, channel) 292 log.Info("Client offline") 293 err := conn.Close() 294 if err != nil { 295 log.Error(err.Error()) 296 } 297 }() 298 299 noVideo := time.NewTimer(10 * time.Second) 300 301 for { 302 select { 303 case <-noVideo.C: 304 return 305 case pck := <-ch: 306 noVideo.Reset(10 * time.Second) 307 _, err := conn.Write(*pck) 308 if err != nil { 309 log.Error(err.Error()) 310 return 311 } 312 } 313 } 314 } 315 316 // handleRTSPServerPlay func 317 func RTSPServerClientResponse(uuid string, channel string, conn net.Conn, status int, headers map[string]string) error { 318 var sdp string 319 builder := bytes.Buffer{} 320 builder.WriteString(fmt.Sprintf(Version+" %d %s\r\n", status, StatusText(status))) 321 for k, v := range headers { 322 if k == "sdp" { 323 sdp = v 324 continue 325 } 326 builder.WriteString(fmt.Sprintf("%s: %s\r\n", k, v)) 327 } 328 builder.WriteString("\r\n") 329 builder.WriteString(sdp) 330 log.Debug(builder.String()) 331 if _, err := conn.Write(builder.Bytes()); err != nil { 332 log.Error(err.Error()) 333 return err 334 } 335 return nil 336 } 337 338 // parsecSEQ func 339 func parsecSEQ(buf []byte) int { 340 return stringToInt(stringInBetween(string(buf), "CSeq: ", "\r\n")) 341 } 342 343 // parseStage func 344 func parseStage(buf []byte) (string, error) { 345 st := strings.Split(string(buf), " ") 346 if len(st) > 0 { 347 return st[0], nil 348 } 349 return "", errors.New("parse stage error " + string(buf)) 350 } 351 352 // parseStreamChannel func 353 func parseStreamChannel(buf []byte) (string, string, string, error) { 354 355 var token string 356 357 uri := stringInBetween(string(buf), " ", " ") 358 u, err := url.Parse(uri) 359 if err == nil { 360 token = u.Query().Get("token") 361 uri = u.Path 362 } 363 364 st := strings.Split(uri, "/") 365 366 if len(st) >= 3 { 367 return st[1], st[2], token, nil 368 } 369 370 return "", "0", token, errors.New("parse stream error " + string(buf)) 371 }