mirror of
https://github.com/Monibuca/plugin-rtsp.git
synced 2025-09-27 03:56:08 +08:00
增加对纯音频的播放的支持
This commit is contained in:
94
client.go
94
client.go
@@ -30,6 +30,7 @@ func (rtsp *RTSP) PullStream(streamPath string, rtspUrl string) (err error) {
|
|||||||
rtsp.aRTPChannel = 2
|
rtsp.aRTPChannel = 2
|
||||||
rtsp.aRTPControlChannel = 3
|
rtsp.aRTPControlChannel = 3
|
||||||
rtsp.URL = rtspUrl
|
rtsp.URL = rtspUrl
|
||||||
|
rtsp.UDPServer = &UDPServer{Session: rtsp}
|
||||||
if err = rtsp.requestStream(); err != nil {
|
if err = rtsp.requestStream(); err != nil {
|
||||||
Println(err)
|
Println(err)
|
||||||
rtsp.Close()
|
rtsp.Close()
|
||||||
@@ -189,86 +190,65 @@ func (client *RTSP) requestStream() (err error) {
|
|||||||
}
|
}
|
||||||
client.SDPRaw = resp.Body
|
client.SDPRaw = resp.Body
|
||||||
client.SDPMap = ParseSDP(client.SDPRaw)
|
client.SDPMap = ParseSDP(client.SDPRaw)
|
||||||
|
client.VSdp, client.HasVideo = client.SDPMap["video"]
|
||||||
|
client.ASdp, client.HasAudio = client.SDPMap["audio"]
|
||||||
session := ""
|
session := ""
|
||||||
if videoInfo, ok := client.SDPMap["video"]; ok {
|
otherChannel := 4
|
||||||
for i := 0; i < len(videoInfo.Control); i++ {
|
for t, sdpInfo := range client.SDPMap {
|
||||||
client.VControl = videoInfo.Control[i]
|
headers = make(map[string]string)
|
||||||
client.VCodec = videoInfo.Codec
|
if session != "" {
|
||||||
client.WriteSPS(videoInfo.SpropParameterSets[0])
|
headers["Session"] = session
|
||||||
client.WritePPS(videoInfo.SpropParameterSets[1])
|
}
|
||||||
var _url = ""
|
var _url = sdpInfo.Control
|
||||||
if strings.Index(strings.ToLower(client.VControl), "rtsp://") == 0 {
|
if !strings.HasPrefix(strings.ToLower(sdpInfo.Control), "rtsp://") {
|
||||||
_url = client.VControl
|
_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(sdpInfo.Control, "/")
|
||||||
} else {
|
}
|
||||||
_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.VControl, "/")
|
switch t {
|
||||||
|
case "video":
|
||||||
|
if len(sdpInfo.SpropParameterSets) > 1 {
|
||||||
|
client.WriteSPS(sdpInfo.SpropParameterSets[0])
|
||||||
|
client.WritePPS(sdpInfo.SpropParameterSets[1])
|
||||||
}
|
}
|
||||||
headers = make(map[string]string)
|
|
||||||
if client.TransType == TRANS_TYPE_TCP {
|
if client.TransType == TRANS_TYPE_TCP {
|
||||||
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel)
|
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel)
|
||||||
} else {
|
} else {
|
||||||
if client.UDPServer == nil {
|
|
||||||
client.UDPServer = &UDPServer{Session: client}
|
|
||||||
}
|
|
||||||
//RTP/AVP;unicast;client_port=64864-64865
|
//RTP/AVP;unicast;client_port=64864-64865
|
||||||
err = client.UDPServer.SetupVideo()
|
if err = client.UDPServer.SetupVideo(); err != nil {
|
||||||
if err != nil {
|
|
||||||
Printf("Setup video err.%v", err)
|
Printf("Setup video err.%v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.VPort, client.UDPServer.VControlPort)
|
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
|
client.Conn.timeout = 0 // UDP ignore timeout
|
||||||
}
|
}
|
||||||
if session != "" {
|
case "audio":
|
||||||
headers["Session"] = session
|
if len(sdpInfo.Config) < 2 {
|
||||||
}
|
Printf("Setup audio err codec not support: %s", client.ASdp.Codec)
|
||||||
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
|
|
||||||
}
|
|
||||||
session, _ = resp.Header["Session"].(string)
|
|
||||||
session = strings.Split(session, ";")[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if audioInfo, ok := client.SDPMap["audio"]; ok {
|
|
||||||
for i := 0; i < len(audioInfo.Control); i++ {
|
|
||||||
client.AControl = audioInfo.Control[i]
|
|
||||||
client.ACodec = audioInfo.Codec
|
|
||||||
if len(audioInfo.Config) < 2 {
|
|
||||||
Printf("Setup audio err codec not support: %s", client.ACodec)
|
|
||||||
} else {
|
} else {
|
||||||
client.WriteASC(audioInfo.Config)
|
client.WriteASC(sdpInfo.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 {
|
if client.TransType == TRANS_TYPE_TCP {
|
||||||
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel)
|
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel)
|
||||||
} else {
|
} else {
|
||||||
if client.UDPServer == nil {
|
if err = client.UDPServer.SetupAudio(); err != nil {
|
||||||
client.UDPServer = &UDPServer{Session: client}
|
|
||||||
}
|
|
||||||
err = client.UDPServer.SetupAudio()
|
|
||||||
if err != nil {
|
|
||||||
Printf("Setup audio err.%v", err)
|
Printf("Setup audio err.%v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.APort, client.UDPServer.AControlPort)
|
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
|
client.Conn.timeout = 0 // UDP ignore timeout
|
||||||
}
|
}
|
||||||
if session != "" {
|
default:
|
||||||
headers["Session"] = session
|
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
|
||||||
}
|
}
|
||||||
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
|
|
||||||
}
|
|
||||||
session, _ = resp.Header["Session"].(string)
|
|
||||||
session = strings.Split(session, ";")[0]
|
|
||||||
}
|
}
|
||||||
|
if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session, _ = resp.Header["Session"].(string)
|
||||||
|
session = strings.Split(session, ";")[0]
|
||||||
}
|
}
|
||||||
headers = make(map[string]string)
|
headers = make(map[string]string)
|
||||||
if session != "" {
|
if session != "" {
|
||||||
@@ -290,7 +270,7 @@ func (client *RTSP) startStream() {
|
|||||||
for {
|
for {
|
||||||
Printf("reconnecting:%s in 5 seconds", client.URL)
|
Printf("reconnecting:%s in 5 seconds", client.URL)
|
||||||
select {
|
select {
|
||||||
case <-client.Context.Done():
|
case <-client.Done():
|
||||||
client.Stop()
|
client.Stop()
|
||||||
return
|
return
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
@@ -346,7 +326,7 @@ func (client *RTSP) startStream() {
|
|||||||
pack = &RTPPack{
|
pack = &RTPPack{
|
||||||
Type: RTP_TYPE_AUDIO,
|
Type: RTP_TYPE_AUDIO,
|
||||||
}
|
}
|
||||||
if client.ACodec == "" {
|
if client.ASdp.Codec != "aac" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case client.aRTPControlChannel:
|
case client.aRTPControlChannel:
|
||||||
|
8
main.go
8
main.go
@@ -23,7 +23,7 @@ var config = struct {
|
|||||||
RemoteAddr string
|
RemoteAddr string
|
||||||
Timeout int
|
Timeout int
|
||||||
Reconnect bool
|
Reconnect bool
|
||||||
}{":554", false, "rtsp://localhost/${streamPath}", 0,false}
|
}{":554", false, "rtsp://localhost/${streamPath}", 0, false}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
InstallPlugin(&PluginConfig{
|
InstallPlugin(&PluginConfig{
|
||||||
@@ -131,10 +131,8 @@ type RTSP struct {
|
|||||||
SDPMap map[string]*SDPInfo
|
SDPMap map[string]*SDPInfo
|
||||||
nonce string
|
nonce string
|
||||||
closeOld bool
|
closeOld bool
|
||||||
AControl string
|
ASdp *SDPInfo
|
||||||
VControl string
|
VSdp *SDPInfo
|
||||||
ACodec string
|
|
||||||
VCodec string
|
|
||||||
aacsent bool
|
aacsent bool
|
||||||
Timeout int
|
Timeout int
|
||||||
//tcp channels
|
//tcp channels
|
||||||
|
@@ -11,7 +11,7 @@ type SDPInfo struct {
|
|||||||
AVType string
|
AVType string
|
||||||
Codec string
|
Codec string
|
||||||
TimeScale int
|
TimeScale int
|
||||||
Control []string
|
Control string
|
||||||
Rtpmap int
|
Rtpmap int
|
||||||
Config []byte
|
Config []byte
|
||||||
SpropParameterSets [][]byte
|
SpropParameterSets [][]byte
|
||||||
@@ -31,17 +31,13 @@ func ParseSDP(sdpRaw string) map[string]*SDPInfo {
|
|||||||
switch typeval[0] {
|
switch typeval[0] {
|
||||||
case "m":
|
case "m":
|
||||||
if len(fields) > 0 {
|
if len(fields) > 0 {
|
||||||
switch fields[0] {
|
info = &SDPInfo{AVType: fields[0]}
|
||||||
case "audio", "video":
|
sdpMap[info.AVType] = info
|
||||||
sdpMap[fields[0]] = &SDPInfo{AVType: fields[0]}
|
mfields := strings.Split(fields[1], " ")
|
||||||
info = sdpMap[fields[0]]
|
if len(mfields) >= 3 {
|
||||||
mfields := strings.Split(fields[1], " ")
|
info.PayloadType, _ = strconv.Atoi(mfields[2])
|
||||||
if len(mfields) >= 3 {
|
|
||||||
info.PayloadType, _ = strconv.Atoi(mfields[2])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "a":
|
case "a":
|
||||||
if info != nil {
|
if info != nil {
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
@@ -51,7 +47,7 @@ func ParseSDP(sdpRaw string) map[string]*SDPInfo {
|
|||||||
val := keyval[1]
|
val := keyval[1]
|
||||||
switch key {
|
switch key {
|
||||||
case "control":
|
case "control":
|
||||||
info.Control = append(info.Control, val)
|
info.Control = val
|
||||||
case "rtpmap":
|
case "rtpmap":
|
||||||
info.Rtpmap, _ = strconv.Atoi(val)
|
info.Rtpmap, _ = strconv.Atoi(val)
|
||||||
}
|
}
|
||||||
@@ -60,6 +56,10 @@ func ParseSDP(sdpRaw string) map[string]*SDPInfo {
|
|||||||
if len(keyval) >= 2 {
|
if len(keyval) >= 2 {
|
||||||
key := keyval[0]
|
key := keyval[0]
|
||||||
switch key {
|
switch key {
|
||||||
|
case "PCMA":
|
||||||
|
info.Codec = "pcma"
|
||||||
|
case "PCMU":
|
||||||
|
info.Codec = "pcmu"
|
||||||
case "MPEG4-GENERIC":
|
case "MPEG4-GENERIC":
|
||||||
info.Codec = "aac"
|
info.Codec = "aac"
|
||||||
case "H264":
|
case "H264":
|
||||||
|
34
session.go
34
session.go
@@ -318,19 +318,17 @@ func (session *RTSP) handleRequest(req *Request) {
|
|||||||
session.SDPRaw = req.Body
|
session.SDPRaw = req.Body
|
||||||
session.SDPMap = ParseSDP(req.Body)
|
session.SDPMap = ParseSDP(req.Body)
|
||||||
if session.Publish(streamPath) {
|
if session.Publish(streamPath) {
|
||||||
sdp, ok := session.SDPMap["audio"]
|
var ok bool
|
||||||
if ok {
|
if session.ASdp, ok = session.SDPMap["audio"]; ok {
|
||||||
session.AControl = sdp.Control[0]
|
session.WriteASC(session.ASdp.Config)
|
||||||
session.ACodec = sdp.Codec
|
Printf("audio codec[%s]\n", session.ASdp.Codec)
|
||||||
session.WriteASC(sdp.Config)
|
|
||||||
Printf("audio codec[%s]\n", session.ACodec)
|
|
||||||
}
|
}
|
||||||
if sdp, ok = session.SDPMap["video"]; ok {
|
if session.VSdp, ok = session.SDPMap["video"]; ok {
|
||||||
session.VControl = sdp.Control[0]
|
if len(session.VSdp.SpropParameterSets) > 1 {
|
||||||
session.VCodec = sdp.Codec
|
session.WriteSPS(session.VSdp.SpropParameterSets[0])
|
||||||
session.WriteSPS(sdp.SpropParameterSets[0])
|
session.WritePPS(session.VSdp.SpropParameterSets[1])
|
||||||
session.WritePPS(sdp.SpropParameterSets[1])
|
}
|
||||||
Printf("video codec[%s]\n", session.VCodec)
|
Printf("video codec[%s]\n", session.VSdp.Codec)
|
||||||
}
|
}
|
||||||
session.Stream.Type = "RTSP"
|
session.Stream.Type = "RTSP"
|
||||||
session.RTSPInfo.StreamInfo = &session.Stream.StreamInfo
|
session.RTSPInfo.StreamInfo = &session.Stream.StreamInfo
|
||||||
@@ -379,8 +377,8 @@ func (session *RTSP) handleRequest(req *Request) {
|
|||||||
// return
|
// return
|
||||||
//}
|
//}
|
||||||
vPath := ""
|
vPath := ""
|
||||||
if strings.Index(strings.ToLower(session.VControl), "rtsp://") == 0 {
|
if strings.Index(strings.ToLower(session.VSdp.Control), "rtsp://") == 0 {
|
||||||
vControlUrl, err := url.Parse(session.VControl)
|
vControlUrl, err := url.Parse(session.VSdp.Control)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.StatusCode = 500
|
res.StatusCode = 500
|
||||||
res.Status = "Invalid VControl"
|
res.Status = "Invalid VControl"
|
||||||
@@ -391,12 +389,12 @@ func (session *RTSP) handleRequest(req *Request) {
|
|||||||
}
|
}
|
||||||
vPath = vControlUrl.String()
|
vPath = vControlUrl.String()
|
||||||
} else {
|
} else {
|
||||||
vPath = session.VControl
|
vPath = session.VSdp.Control
|
||||||
}
|
}
|
||||||
|
|
||||||
aPath := ""
|
aPath := ""
|
||||||
if strings.Index(strings.ToLower(session.AControl), "rtsp://") == 0 {
|
if strings.Index(strings.ToLower(session.ASdp.Control), "rtsp://") == 0 {
|
||||||
aControlUrl, err := url.Parse(session.AControl)
|
aControlUrl, err := url.Parse(session.ASdp.Control)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.StatusCode = 500
|
res.StatusCode = 500
|
||||||
res.Status = "Invalid AControl"
|
res.Status = "Invalid AControl"
|
||||||
@@ -407,7 +405,7 @@ func (session *RTSP) handleRequest(req *Request) {
|
|||||||
}
|
}
|
||||||
aPath = aControlUrl.String()
|
aPath = aControlUrl.String()
|
||||||
} else {
|
} else {
|
||||||
aPath = session.AControl
|
aPath = session.ASdp.Control
|
||||||
}
|
}
|
||||||
|
|
||||||
mtcp := regexp.MustCompile("interleaved=(\\d+)(-(\\d+))?")
|
mtcp := regexp.MustCompile("interleaved=(\\d+)(-(\\d+))?")
|
||||||
|
Reference in New Issue
Block a user