Browse Source

support receiving single tracks (#48)

pull/54/head
aler9 5 years ago
parent
commit
d0c2c3a586
  1. 93
      client.go
  2. 41
      main.go
  3. 2
      utils.go

93
client.go

@ -7,6 +7,7 @@ import (
"net" "net"
"os" "os"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
"time" "time"
@ -88,7 +89,7 @@ type client struct {
authHelper *gortsplib.AuthServer authHelper *gortsplib.AuthServer
authFailures int authFailures int
streamProtocol gortsplib.StreamProtocol streamProtocol gortsplib.StreamProtocol
streamTracks []*clientTrack streamTracks map[int]*clientTrack
rtcpReceivers []*gortsplib.RtcpReceiver rtcpReceivers []*gortsplib.RtcpReceiver
readBuf *doubleBuffer readBuf *doubleBuffer
writeBuf *doubleBuffer writeBuf *doubleBuffer
@ -107,6 +108,7 @@ func newClient(p *program, nconn net.Conn) *client {
WriteTimeout: p.conf.WriteTimeout, WriteTimeout: p.conf.WriteTimeout,
}), }),
state: clientStateInitial, state: clientStateInitial,
streamTracks: make(map[int]*clientTrack),
readBuf: newDoubleBuffer(clientTcpReadBufferSize), readBuf: newDoubleBuffer(clientTcpReadBufferSize),
done: make(chan struct{}), done: make(chan struct{}),
} }
@ -433,7 +435,7 @@ func (c *client) handleRequest(req *gortsplib.Request) bool {
return false return false
} }
basePath, _, err := splitPath(path) basePath, controlPath, err := splitPath(path)
if err != nil { if err != nil {
c.writeResError(req, gortsplib.StatusBadRequest, err) c.writeResError(req, gortsplib.StatusBadRequest, err)
return false return false
@ -457,6 +459,28 @@ func (c *client) handleRequest(req *gortsplib.Request) bool {
return true return true
} }
if c.pathId != "" && basePath != c.pathId {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("path has changed, was '%s', now is '%s'", c.pathId, basePath))
return false
}
if !strings.HasPrefix(controlPath, "trackID=") {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("invalid control path (%s)", controlPath))
return false
}
tmp, err := strconv.ParseInt(controlPath[len("trackID="):], 10, 64)
if err != nil || tmp < 0 {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("invalid track id (%s)", controlPath))
return false
}
trackId := int(tmp)
if _, ok := c.streamTracks[trackId]; ok {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("track %d has already been setup", trackId))
return false
}
// play via UDP // play via UDP
if func() bool { if func() bool {
_, ok := th["RTP/AVP"] _, ok := th["RTP/AVP"]
@ -474,30 +498,31 @@ func (c *client) handleRequest(req *gortsplib.Request) bool {
return false return false
} }
rtpPort, rtcpPort := th.Ports("client_port") if len(c.streamTracks) > 0 && c.streamProtocol != gortsplib.StreamProtocolUdp {
if rtpPort == 0 || rtcpPort == 0 { c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("can't receive tracks with different protocols"))
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("transport header does not have valid client ports (%v)", req.Header["Transport"]))
return false
}
if c.pathId != "" && basePath != c.pathId {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("path has changed, was '%s', now is '%s'", c.pathId, basePath))
return false return false
} }
if len(c.streamTracks) > 0 && c.streamProtocol != gortsplib.StreamProtocolUdp { rtpPort, rtcpPort := th.Ports("client_port")
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("can't receive tracks with different protocols")) if rtpPort == 0 || rtcpPort == 0 {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("transport header does not have valid client ports (%v)", req.Header["Transport"]))
return false return false
} }
res := make(chan error) res := make(chan error)
c.p.events <- programEventClientSetupPlay{res, c, basePath, gortsplib.StreamProtocolUdp, rtpPort, rtcpPort} c.p.events <- programEventClientSetupPlay{res, c, basePath, trackId}
err = <-res err = <-res
if err != nil { if err != nil {
c.writeResError(req, gortsplib.StatusBadRequest, err) c.writeResError(req, gortsplib.StatusBadRequest, err)
return false return false
} }
c.streamProtocol = gortsplib.StreamProtocolUdp
c.streamTracks[trackId] = &clientTrack{
rtpPort: rtpPort,
rtcpPort: rtcpPort,
}
c.conn.WriteResponse(&gortsplib.Response{ c.conn.WriteResponse(&gortsplib.Response{
StatusCode: gortsplib.StatusOK, StatusCode: gortsplib.StatusOK,
Header: gortsplib.Header{ Header: gortsplib.Header{
@ -520,25 +545,26 @@ func (c *client) handleRequest(req *gortsplib.Request) bool {
return false return false
} }
if c.pathId != "" && basePath != c.pathId {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("path has changed, was '%s', now is '%s'", c.pathId, basePath))
return false
}
if len(c.streamTracks) > 0 && c.streamProtocol != gortsplib.StreamProtocolTcp { if len(c.streamTracks) > 0 && c.streamProtocol != gortsplib.StreamProtocolTcp {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("can't receive tracks with different protocols")) c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("can't receive tracks with different protocols"))
return false return false
} }
res := make(chan error) res := make(chan error)
c.p.events <- programEventClientSetupPlay{res, c, basePath, gortsplib.StreamProtocolTcp, 0, 0} c.p.events <- programEventClientSetupPlay{res, c, basePath, trackId}
err = <-res err = <-res
if err != nil { if err != nil {
c.writeResError(req, gortsplib.StatusBadRequest, err) c.writeResError(req, gortsplib.StatusBadRequest, err)
return false return false
} }
interleaved := fmt.Sprintf("%d-%d", ((len(c.streamTracks) - 1) * 2), ((len(c.streamTracks)-1)*2)+1) c.streamProtocol = gortsplib.StreamProtocolTcp
c.streamTracks[trackId] = &clientTrack{
rtpPort: 0,
rtcpPort: 0,
}
interleaved := fmt.Sprintf("%d-%d", ((trackId) * 2), ((trackId)*2)+1)
c.conn.WriteResponse(&gortsplib.Response{ c.conn.WriteResponse(&gortsplib.Response{
StatusCode: gortsplib.StatusOK, StatusCode: gortsplib.StatusOK,
@ -589,14 +615,14 @@ func (c *client) handleRequest(req *gortsplib.Request) bool {
return false return false
} }
rtpPort, rtcpPort := th.Ports("client_port") if len(c.streamTracks) > 0 && c.streamProtocol != gortsplib.StreamProtocolUdp {
if rtpPort == 0 || rtcpPort == 0 { c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("can't publish tracks with different protocols"))
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("transport header does not have valid client ports (%s)", req.Header["Transport"]))
return false return false
} }
if len(c.streamTracks) > 0 && c.streamProtocol != gortsplib.StreamProtocolUdp { rtpPort, rtcpPort := th.Ports("client_port")
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("can't publish tracks with different protocols")) if rtpPort == 0 || rtcpPort == 0 {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("transport header does not have valid client ports (%s)", req.Header["Transport"]))
return false return false
} }
@ -606,13 +632,19 @@ func (c *client) handleRequest(req *gortsplib.Request) bool {
} }
res := make(chan error) res := make(chan error)
c.p.events <- programEventClientSetupRecord{res, c, gortsplib.StreamProtocolUdp, rtpPort, rtcpPort} c.p.events <- programEventClientSetupRecord{res, c}
err := <-res err := <-res
if err != nil { if err != nil {
c.writeResError(req, gortsplib.StatusBadRequest, err) c.writeResError(req, gortsplib.StatusBadRequest, err)
return false return false
} }
c.streamProtocol = gortsplib.StreamProtocolUdp
c.streamTracks[len(c.streamTracks)] = &clientTrack{
rtpPort: rtpPort,
rtcpPort: rtcpPort,
}
c.conn.WriteResponse(&gortsplib.Response{ c.conn.WriteResponse(&gortsplib.Response{
StatusCode: gortsplib.StatusOK, StatusCode: gortsplib.StatusOK,
Header: gortsplib.Header{ Header: gortsplib.Header{
@ -658,13 +690,19 @@ func (c *client) handleRequest(req *gortsplib.Request) bool {
} }
res := make(chan error) res := make(chan error)
c.p.events <- programEventClientSetupRecord{res, c, gortsplib.StreamProtocolTcp, 0, 0} c.p.events <- programEventClientSetupRecord{res, c}
err := <-res err := <-res
if err != nil { if err != nil {
c.writeResError(req, gortsplib.StatusBadRequest, err) c.writeResError(req, gortsplib.StatusBadRequest, err)
return false return false
} }
c.streamProtocol = gortsplib.StreamProtocolTcp
c.streamTracks[len(c.streamTracks)] = &clientTrack{
rtpPort: 0,
rtcpPort: 0,
}
c.conn.WriteResponse(&gortsplib.Response{ c.conn.WriteResponse(&gortsplib.Response{
StatusCode: gortsplib.StatusOK, StatusCode: gortsplib.StatusOK,
Header: gortsplib.Header{ Header: gortsplib.Header{
@ -776,6 +814,7 @@ func (c *client) runPlay(path string) {
c.events = make(chan clientEvent) c.events = make(chan clientEvent)
} }
// start sending frames only after sending the response to the PLAY request
done := make(chan struct{}) done := make(chan struct{})
c.p.events <- programEventClientPlay2{done, c} c.p.events <- programEventClientPlay2{done, c}
<-done <-done

41
main.go

@ -73,9 +73,7 @@ type programEventClientSetupPlay struct {
res chan error res chan error
client *client client *client
path string path string
protocol gortsplib.StreamProtocol trackId int
rtpPort int
rtcpPort int
} }
func (programEventClientSetupPlay) isProgramEvent() {} func (programEventClientSetupPlay) isProgramEvent() {}
@ -83,9 +81,6 @@ func (programEventClientSetupPlay) isProgramEvent() {}
type programEventClientSetupRecord struct { type programEventClientSetupRecord struct {
res chan error res chan error
client *client client *client
protocol gortsplib.StreamProtocol
rtpPort int
rtcpPort int
} }
func (programEventClientSetupRecord) isProgramEvent() {} func (programEventClientSetupRecord) isProgramEvent() {}
@ -384,26 +379,16 @@ outer:
continue continue
} }
if len(evt.client.streamTracks) >= len(path.publisherSdpParsed.MediaDescriptions) { if evt.trackId >= len(path.publisherSdpParsed.MediaDescriptions) {
evt.res <- fmt.Errorf("all the tracks have already been setup") evt.res <- fmt.Errorf("track %d does not exist", evt.trackId)
continue continue
} }
evt.client.pathId = evt.path evt.client.pathId = evt.path
evt.client.streamProtocol = evt.protocol
evt.client.streamTracks = append(evt.client.streamTracks, &clientTrack{
rtpPort: evt.rtpPort,
rtcpPort: evt.rtcpPort,
})
evt.client.state = clientStatePrePlay evt.client.state = clientStatePrePlay
evt.res <- nil evt.res <- nil
case programEventClientSetupRecord: case programEventClientSetupRecord:
evt.client.streamProtocol = evt.protocol
evt.client.streamTracks = append(evt.client.streamTracks, &clientTrack{
rtpPort: evt.rtpPort,
rtcpPort: evt.rtcpPort,
})
evt.client.state = clientStatePreRecord evt.client.state = clientStatePreRecord
evt.res <- nil evt.res <- nil
@ -414,8 +399,8 @@ outer:
continue continue
} }
if len(evt.client.streamTracks) != len(path.publisherSdpParsed.MediaDescriptions) { if len(evt.client.streamTracks) == 0 {
evt.res <- fmt.Errorf("not all tracks have been setup") evt.res <- fmt.Errorf("no tracks have been setup")
continue continue
} }
@ -589,14 +574,23 @@ func (p *program) findClientPublisher(addr *net.UDPAddr, streamType gortsplib.St
func (p *program) forwardFrame(path string, trackId int, streamType gortsplib.StreamType, frame []byte) { func (p *program) forwardFrame(path string, trackId int, streamType gortsplib.StreamType, frame []byte) {
for c := range p.clients { for c := range p.clients {
if c.pathId == path && c.state == clientStatePlay { if c.pathId != path ||
c.state != clientStatePlay {
continue
}
track, ok := c.streamTracks[trackId]
if !ok {
continue
}
if c.streamProtocol == gortsplib.StreamProtocolUdp { if c.streamProtocol == gortsplib.StreamProtocolUdp {
if streamType == gortsplib.StreamTypeRtp { if streamType == gortsplib.StreamTypeRtp {
p.serverRtp.write(&udpAddrBufPair{ p.serverRtp.write(&udpAddrBufPair{
addr: &net.UDPAddr{ addr: &net.UDPAddr{
IP: c.ip(), IP: c.ip(),
Zone: c.zone(), Zone: c.zone(),
Port: c.streamTracks[trackId].rtpPort, Port: track.rtpPort,
}, },
buf: frame, buf: frame,
}) })
@ -605,7 +599,7 @@ func (p *program) forwardFrame(path string, trackId int, streamType gortsplib.St
addr: &net.UDPAddr{ addr: &net.UDPAddr{
IP: c.ip(), IP: c.ip(),
Zone: c.zone(), Zone: c.zone(),
Port: c.streamTracks[trackId].rtcpPort, Port: track.rtcpPort,
}, },
buf: frame, buf: frame,
}) })
@ -626,7 +620,6 @@ func (p *program) forwardFrame(path string, trackId int, streamType gortsplib.St
} }
} }
} }
}
func main() { func main() {
_, err := newProgram(os.Args[1:], os.Stdin) _, err := newProgram(os.Args[1:], os.Stdin)

2
utils.go

@ -110,7 +110,7 @@ func sdpForServer(tracks []*gortsplib.Track) (*sdp.SessionDescription, []byte) {
} }
} }
// control attribute is mandatory, and is the path that is appended // control attribute is the path that is appended
// to the stream path in SETUP // to the stream path in SETUP
ret = append(ret, sdp.Attribute{ ret = append(ret, sdp.Attribute{
Key: "control", Key: "control",

Loading…
Cancel
Save