mirror of
https://github.com/Monibuca/plugin-rtsp.git
synced 2025-09-26 19:51:14 +08:00
Merge branch 'master' of https://github.com/ourfor-pp/plugin-rtsp
This commit is contained in:
114
client.go
114
client.go
@@ -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
2
go.mod
@@ -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
4
go.sum
@@ -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=
|
||||
|
8
main.go
8
main.go
@@ -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
|
||||
|
@@ -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":
|
||||
|
34
session.go
34
session.go
@@ -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+))?")
|
||||
|
Reference in New Issue
Block a user