This commit is contained in:
mqh
2020-08-28 09:33:53 +08:00
6 changed files with 82 additions and 98 deletions

114
client.go
View File

@@ -30,6 +30,7 @@ func (rtsp *RTSP) PullStream(streamPath string, rtspUrl string) (err error) {
rtsp.aRTPChannel = 2
rtsp.aRTPControlChannel = 3
rtsp.URL = rtspUrl
rtsp.UDPServer = &UDPServer{Session: rtsp}
if err = rtsp.requestStream(); err != nil {
Println(err)
rtsp.Close()
@@ -144,7 +145,7 @@ func (client *RTSP) requestStream() (err error) {
client.connRW = bufio.NewReadWriter(bufio.NewReaderSize(&timeoutConn, networkBuffer), bufio.NewWriterSize(&timeoutConn, networkBuffer))
headers := make(map[string]string)
headers["Require"] = "implicit-play"
//headers["Require"] = "implicit-play"
// An OPTIONS request returns the request types the server will accept.
resp, err := client.Request("OPTIONS", headers)
if err != nil {
@@ -189,77 +190,60 @@ func (client *RTSP) requestStream() (err error) {
}
client.SDPRaw = resp.Body
client.SDPMap = ParseSDP(client.SDPRaw)
client.VSdp, client.HasVideo = client.SDPMap["video"]
client.ASdp, client.HasAudio = client.SDPMap["audio"]
session := ""
if videoInfo, ok := client.SDPMap["video"]; ok {
client.VControl = videoInfo.Control
client.VCodec = videoInfo.Codec
client.WriteSPS(videoInfo.SpropParameterSets[0])
client.WritePPS(videoInfo.SpropParameterSets[1])
var _url = ""
if strings.Index(strings.ToLower(client.VControl), "rtsp://") == 0 {
_url = client.VControl
} else {
_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.VControl, "/")
}
otherChannel := 4
for t, sdpInfo := range client.SDPMap {
headers = make(map[string]string)
if client.TransType == TRANS_TYPE_TCP {
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel)
} else {
if client.UDPServer == nil {
client.UDPServer = &UDPServer{Session: client}
}
//RTP/AVP;unicast;client_port=64864-64865
err = client.UDPServer.SetupVideo()
if err != nil {
Printf("Setup video err.%v", err)
return err
}
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.VPort, client.UDPServer.VControlPort)
client.Conn.timeout = 0 // UDP ignore timeout
}
if session != "" {
headers["Session"] = session
}
Printf("Parse DESCRIBE response, VIDEO VControl:%s, VCode:%s, url:%s,Session:%s,vRTPChannel:%d,vRTPControlChannel:%d", client.VControl, client.VCodec, _url, session, client.vRTPChannel, client.vRTPControlChannel)
if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil {
return err
var _url = sdpInfo.Control
if !strings.HasPrefix(strings.ToLower(sdpInfo.Control), "rtsp://") {
_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(sdpInfo.Control, "/")
}
session, _ = resp.Header["Session"].(string)
session = strings.Split(session, ";")[0]
}
if audioInfo, ok := client.SDPMap["audio"]; ok {
client.AControl = audioInfo.Control
client.ACodec = audioInfo.Codec
if len(audioInfo.Config) < 2 {
Printf("Setup audio err codec not support: %s", client.ACodec)
} else {
client.WriteASC(audioInfo.Config)
}
var _url = ""
if strings.Index(strings.ToLower(client.AControl), "rtsp://") == 0 {
_url = client.AControl
} else {
_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.AControl, "/")
}
headers = make(map[string]string)
if client.TransType == TRANS_TYPE_TCP {
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel)
} else {
if client.UDPServer == nil {
client.UDPServer = &UDPServer{Session: client}
switch t {
case "video":
if len(sdpInfo.SpropParameterSets) > 1 {
client.WriteSPS(sdpInfo.SpropParameterSets[0])
client.WritePPS(sdpInfo.SpropParameterSets[1])
}
err = client.UDPServer.SetupAudio()
if err != nil {
Printf("Setup audio err.%v", err)
return err
if client.TransType == TRANS_TYPE_TCP {
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel)
} else {
//RTP/AVP;unicast;client_port=64864-64865
if err = client.UDPServer.SetupVideo(); err != nil {
Printf("Setup video err.%v", err)
return err
}
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.VPort, client.UDPServer.VControlPort)
client.Conn.timeout = 0 // UDP ignore timeout
}
case "audio":
if len(sdpInfo.Config) < 2 {
Printf("Setup audio err codec not support: %s", client.ASdp.Codec)
} else {
client.WriteASC(sdpInfo.Config)
}
if client.TransType == TRANS_TYPE_TCP {
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel)
} else {
if err = client.UDPServer.SetupAudio(); err != nil {
Printf("Setup audio err.%v", err)
return err
}
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.APort, client.UDPServer.AControlPort)
client.Conn.timeout = 0 // UDP ignore timeout
}
default:
if client.TransType == TRANS_TYPE_TCP {
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", otherChannel, otherChannel+1)
otherChannel += 2
} else {
//TODO: UDP support
}
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.APort, client.UDPServer.AControlPort)
client.Conn.timeout = 0 // UDP ignore timeout
}
if session != "" {
headers["Session"] = session
}
Printf("Parse DESCRIBE response, AUDIO AControl:%s, ACodec:%s, url:%s,Session:%s, aRTPChannel:%d,aRTPControlChannel:%d", client.AControl, client.ACodec, _url, session, client.aRTPChannel, client.aRTPControlChannel)
if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil {
return err
}
@@ -286,7 +270,7 @@ func (client *RTSP) startStream() {
for {
Printf("reconnecting:%s in 5 seconds", client.URL)
select {
case <-client.Context.Done():
case <-client.Done():
client.Stop()
return
case <-t.C:
@@ -342,7 +326,7 @@ func (client *RTSP) startStream() {
pack = &RTPPack{
Type: RTP_TYPE_AUDIO,
}
if client.ACodec == "" {
if client.ASdp.Codec != "aac" {
continue
}
case client.aRTPControlChannel:

2
go.mod
View File

@@ -5,6 +5,6 @@ go 1.13
require (
github.com/Monibuca/engine/v2 v2.1.2
github.com/Monibuca/plugin-rtp v0.0.0-20200531014802-504413c0dfcb
github.com/pion/rtp v1.5.4 // indirect
github.com/pion/rtp v1.6.0 // indirect
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
)

4
go.sum
View File

@@ -22,8 +22,12 @@ github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+v
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pion/randutil v0.0.0 h1:aLWLVhTG2jzoD25F0OlW6nXvXrjoGwiXq2Sz7j7NzL0=
github.com/pion/randutil v0.0.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtp v1.5.4 h1:PuNg6xqV3brIUihatcKZj1YDUs+M45L0ZbrZWYtkDxY=
github.com/pion/rtp v1.5.4/go.mod h1:bg60AL5GotNOlYZsqycbhDtEV3TkfbpXG0KBiUq29Mg=
github.com/pion/rtp v1.6.0 h1:4Ssnl/T5W2LzxHj9ssYpGVEQh3YYhQFNVmSWO88MMwk=
github.com/pion/rtp v1.6.0/go.mod h1:QgfogHsMBVE/RFNno467U/KBqfUywEH+HK+0rtnwsdI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View File

@@ -23,7 +23,7 @@ var config = struct {
RemoteAddr string
Timeout int
Reconnect bool
}{":554", false, "rtsp://localhost/${streamPath}", 0,false}
}{":554", false, "rtsp://localhost/${streamPath}", 0, false}
func init() {
InstallPlugin(&PluginConfig{
@@ -131,10 +131,8 @@ type RTSP struct {
SDPMap map[string]*SDPInfo
nonce string
closeOld bool
AControl string
VControl string
ACodec string
VCodec string
ASdp *SDPInfo
VSdp *SDPInfo
aacsent bool
Timeout int
//tcp channels

View File

@@ -31,17 +31,13 @@ func ParseSDP(sdpRaw string) map[string]*SDPInfo {
switch typeval[0] {
case "m":
if len(fields) > 0 {
switch fields[0] {
case "audio", "video":
sdpMap[fields[0]] = &SDPInfo{AVType: fields[0]}
info = sdpMap[fields[0]]
mfields := strings.Split(fields[1], " ")
if len(mfields) >= 3 {
info.PayloadType, _ = strconv.Atoi(mfields[2])
}
info = &SDPInfo{AVType: fields[0]}
sdpMap[info.AVType] = info
mfields := strings.Split(fields[1], " ")
if len(mfields) >= 3 {
info.PayloadType, _ = strconv.Atoi(mfields[2])
}
}
case "a":
if info != nil {
for _, field := range fields {
@@ -60,6 +56,10 @@ func ParseSDP(sdpRaw string) map[string]*SDPInfo {
if len(keyval) >= 2 {
key := keyval[0]
switch key {
case "PCMA":
info.Codec = "pcma"
case "PCMU":
info.Codec = "pcmu"
case "MPEG4-GENERIC":
info.Codec = "aac"
case "H264":

View File

@@ -318,19 +318,17 @@ func (session *RTSP) handleRequest(req *Request) {
session.SDPRaw = req.Body
session.SDPMap = ParseSDP(req.Body)
if session.Publish(streamPath) {
sdp, ok := session.SDPMap["audio"]
if ok {
session.AControl = sdp.Control
session.ACodec = sdp.Codec
session.WriteASC(sdp.Config)
Printf("audio codec[%s]\n", session.ACodec)
var ok bool
if session.ASdp, ok = session.SDPMap["audio"]; ok {
session.WriteASC(session.ASdp.Config)
Printf("audio codec[%s]\n", session.ASdp.Codec)
}
if sdp, ok = session.SDPMap["video"]; ok {
session.VControl = sdp.Control
session.VCodec = sdp.Codec
session.WriteSPS(sdp.SpropParameterSets[0])
session.WritePPS(sdp.SpropParameterSets[1])
Printf("video codec[%s]\n", session.VCodec)
if session.VSdp, ok = session.SDPMap["video"]; ok {
if len(session.VSdp.SpropParameterSets) > 1 {
session.WriteSPS(session.VSdp.SpropParameterSets[0])
session.WritePPS(session.VSdp.SpropParameterSets[1])
}
Printf("video codec[%s]\n", session.VSdp.Codec)
}
session.Stream.Type = "RTSP"
session.RTSPInfo.StreamInfo = &session.Stream.StreamInfo
@@ -379,8 +377,8 @@ func (session *RTSP) handleRequest(req *Request) {
// return
//}
vPath := ""
if strings.Index(strings.ToLower(session.VControl), "rtsp://") == 0 {
vControlUrl, err := url.Parse(session.VControl)
if strings.Index(strings.ToLower(session.VSdp.Control), "rtsp://") == 0 {
vControlUrl, err := url.Parse(session.VSdp.Control)
if err != nil {
res.StatusCode = 500
res.Status = "Invalid VControl"
@@ -391,12 +389,12 @@ func (session *RTSP) handleRequest(req *Request) {
}
vPath = vControlUrl.String()
} else {
vPath = session.VControl
vPath = session.VSdp.Control
}
aPath := ""
if strings.Index(strings.ToLower(session.AControl), "rtsp://") == 0 {
aControlUrl, err := url.Parse(session.AControl)
if strings.Index(strings.ToLower(session.ASdp.Control), "rtsp://") == 0 {
aControlUrl, err := url.Parse(session.ASdp.Control)
if err != nil {
res.StatusCode = 500
res.Status = "Invalid AControl"
@@ -407,7 +405,7 @@ func (session *RTSP) handleRequest(req *Request) {
}
aPath = aControlUrl.String()
} else {
aPath = session.AControl
aPath = session.ASdp.Control
}
mtcp := regexp.MustCompile("interleaved=(\\d+)(-(\\d+))?")