diff --git a/pkg/rtsp/packer.go b/pkg/rtsp/packer.go index 41fc62e..bd9f998 100644 --- a/pkg/rtsp/packer.go +++ b/pkg/rtsp/packer.go @@ -13,14 +13,105 @@ import ( "time" ) -var ResponseOptionsTmpl = "RTSP/1.0 200 OK\r\nCSeq: %s\r\nPublic:DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n\r\n" -var ResponseSetupTmpl = "RTSP/1.0 200 OK\r\nCSeq: %s\r\nDate: %s\r\nSession: 47112344\r\nTransport:%s;server_port=6256-6257" +// VLC rtsp://127.0.0.1:5544/vlc +// handleTCPConnect. conn=0xc000010030 - server.go:50 +// requestLine=OPTIONS rtsp://127.0.0.1:5544/vlc RTSP/1.0, headers=map[CSeq:2 User-Agent:LibVLC/3.0.8 (LIVE555 Streaming Media v2016.11.28)] - server.go:58 +// requestLine=DESCRIBE rtsp://127.0.0.1:5544/vlc RTSP/1.0, headers=map[Accept:application/sdp CSeq:3 User-Agent:LibVLC/3.0.8 (LIVE555 Streaming Media v2016.11.28)] - server.go:58 +// EOF - server.go:55 +// handleTCPConnect. conn=0xc000010038 - server.go:50 +// requestLine=SETUP rtsp://127.0.0.1:5544/vlc RTSP/1.0, headers=map[CSeq:0 Transport:RTP/AVP;unicast;client_port=9300-9301] - server.go:58 +// requestLine=PLAY rtsp://127.0.0.1:5544/stream=0 RTSP/1.0, headers=map[CSeq:1 Session:47112344] - server.go:58 + +// ffmpeg +// handleTCPConnect. conn=0xc000010030 - server.go:52 +// requestLine=OPTIONS rtsp://localhost:5544/test110 RTSP/1.0, headers=map[CSeq:1 User-Agent:Lavf57.83.100] - server.go:60 +// requestLine=ANNOUNCE rtsp://localhost:5544/test110 RTSP/1.0, headers=map[CSeq:2 Content-Length:280 Content-Type:application/sdp User-Agent:Lavf57.83.100] - server.go:60 +// body=v=0 +// o=- 0 0 IN IP6 ::1 +// s=No Name +// c=IN IP6 ::1 +// t=0 0 +// a=tool:libavformat 57.83.100 +// m=video 0 RTP/AVP 96 +// a=rtpmap:96 H264/90000 +// a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKKzZQHgCJ+XARAAAAwAEAAADACg8YMZY,aOvjyyLA; profile-level-id=640028 +// a=control:streamid=0 +// - server.go:76 + +// [DONE] +// rfc2326 10.1 OPTIONS +// CSeq +var ResponseOptionsTmpl = "RTSP/1.0 200 OK\r\n" + + "Server: " + serverName + "\r\n" + + "CSeq: %s\r\n" + + "Public:DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n\r\n" + +// rfc2326 10.2 DESCRIBE +// CSeq, Date, Content-Length, +var ResponseDescribeTmpl = "RTSP/1.0 200 OK\r\n" + + "CSeq: %s\r\n" + + "Date: %s\r\n" + + "Content-Type: application/sdp\r\n" + + "Content-Length: %d\r\n\r\n" + + SDPTmpl + +// rfc4566 +// * v=0 版本号,固定 +// * o= +// +// * t= +// * a= +var SDPTmpl = "v=0\r\n" + + "o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4\r\n" + + //"s=SDP Seminar\r\n" + + //"i=A Seminar on the session description protocol\r\n" + + //"u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps\r\n" + + //"e=mjh@isi.edu (Mark Handley)\r\n" + + //"c=IN IP4 224.2.17.12/127\r\n" + + //"t=2873397496 2873404696\r\n" + + "t=0 0\r\n" + + "a=recvonly\r\n" + //"m=audio 3456 RTP/AVP 0\r\n" + + //"m=video 2232 RTP/AVP 31\r\n" + + //"m=whiteboard 32416 UDP WB\r\n" + + //"a=orient:portrait\r\n" + +// rfc2326 10.4 SETUP +// CSeq, Date, Transport +// TODO chef: server_port +var ResponseSetupTmpl = "RTSP/1.0 200 OK\r\n" + + "CSeq: %s\r\n" + + "Date: %s\r\n" + + "Session: 47112344\r\n" + + "Transport:%s;server_port=6256-6257\r\n\r\n" + +// rfc2326 10.5 PLAY +var PlayTmpl = "RTSP/1.0 200 OK\r\n" + + "CSeq: %s\r\n" + + "Date: %s\r\n" + +var AnnounceTmpl = "RTSP/1.0 200 OK\r\n" + + "CSeq: %s\r\n" func PackResponseOptions(cseq string) string { return fmt.Sprintf(ResponseOptionsTmpl, cseq) } +func PackResponseDescribe(cseq string) string { + date := time.Now().Format(time.RFC1123) + return fmt.Sprintf(ResponseDescribeTmpl, cseq, date, 376) +} + func PackResponseSetup(cseq string, transportC string) string { date := time.Now().Format(time.RFC1123) return fmt.Sprintf(ResponseSetupTmpl, cseq, date, transportC) } + +func PackResponsePlay(cseq string) string { + date := time.Now().Format(time.RFC1123) + return fmt.Sprintf(PlayTmpl, cseq, date) +} + +func PackResponseAnnounce(cseq string) string { + return fmt.Sprintf(AnnounceTmpl, cseq) +} diff --git a/pkg/rtsp/rtsp.go b/pkg/rtsp/rtsp.go index 80028df..38dc236 100644 --- a/pkg/rtsp/rtsp.go +++ b/pkg/rtsp/rtsp.go @@ -13,11 +13,19 @@ package rtsp // rfc2326 const ( - MethodOptions = "OPTIONS" - MethodSetup = "SETUP" + MethodOptions = "OPTIONS" + MethodDescribe = "DESCRIBE" + MethodSetup = "SETUP" + MethodPlay = "PLAY" + + MethodAnnounce = "ANNOUNCE" ) const ( HeaderFieldCSeq = "CSeq" HeaderFieldTransport = "Transport" ) + +// TODO chef: +// 收集lal中其他可以hack服务名的地方,统一到一处,并增加版本号信息 +const serverName = "lalserver" diff --git a/pkg/rtsp/server.go b/pkg/rtsp/server.go index b38b84e..aae3ff8 100644 --- a/pkg/rtsp/server.go +++ b/pkg/rtsp/server.go @@ -10,7 +10,9 @@ package rtsp import ( "bufio" + "io" "net" + "strconv" "github.com/q191201771/naza/pkg/nazahttp" "github.com/q191201771/naza/pkg/nazalog" @@ -63,14 +65,37 @@ func (s *Server) handleTCPConnect(conn net.Conn) { break } + var body []byte + if contentLength, ok := headers["Content-Length"]; ok { + if cl, err := strconv.Atoi(contentLength); err == nil { + body := make([]byte, cl) + l, err := io.ReadAtLeast(r, body, cl) + if l != cl || err != nil { + nazalog.Errorf("read rtsp cmd fail. content-length=%d, read length=%d, err=%+v", cl, l, err) + } else { + nazalog.Debugf("body=%s", string(body)) + } + } + } + _ = body + // TODO chef: header field not exist? switch method { case MethodOptions: resp := PackResponseOptions(headers[HeaderFieldCSeq]) _, _ = conn.Write([]byte(resp)) + case MethodDescribe: + resp := PackResponseDescribe(headers[HeaderFieldCSeq]) + _, _ = conn.Write([]byte(resp)) case MethodSetup: resp := PackResponseSetup(headers[HeaderFieldCSeq], headers[HeaderFieldTransport]) _, _ = conn.Write([]byte(resp)) + case MethodPlay: + resp := PackResponsePlay(headers[HeaderFieldCSeq]) + _, _ = conn.Write([]byte(resp)) + case MethodAnnounce: + resp := PackResponseAnnounce(headers[HeaderFieldCSeq]) + _, _ = conn.Write([]byte(resp)) default: nazalog.Error(method) }