|
|
|
@ -42,7 +42,8 @@ type program struct { |
|
|
|
serverRtcp *serverUDP |
|
|
|
serverRtcp *serverUDP |
|
|
|
serverRtsp *serverTCP |
|
|
|
serverRtsp *serverTCP |
|
|
|
clients map[*client]struct{} |
|
|
|
clients map[*client]struct{} |
|
|
|
udpClientsByAddr map[udpClientAddr]*udpClient |
|
|
|
udpPublishersMap *udpPublishersMap |
|
|
|
|
|
|
|
readersMap *readersMap |
|
|
|
countClient int64 |
|
|
|
countClient int64 |
|
|
|
countPublisher int64 |
|
|
|
countPublisher int64 |
|
|
|
countReader int64 |
|
|
|
countReader int64 |
|
|
|
@ -55,11 +56,8 @@ type program struct { |
|
|
|
clientSetupPlay chan clientSetupPlayReq |
|
|
|
clientSetupPlay chan clientSetupPlayReq |
|
|
|
clientPlay chan *client |
|
|
|
clientPlay chan *client |
|
|
|
clientRecord chan *client |
|
|
|
clientRecord chan *client |
|
|
|
clientFrameUDP chan clientFrameUDPReq |
|
|
|
|
|
|
|
clientFrameTCP chan clientFrameTCPReq |
|
|
|
|
|
|
|
sourceReady chan *source |
|
|
|
sourceReady chan *source |
|
|
|
sourceNotReady chan *source |
|
|
|
sourceNotReady chan *source |
|
|
|
sourceFrame chan sourceFrameReq |
|
|
|
|
|
|
|
terminate chan struct{} |
|
|
|
terminate chan struct{} |
|
|
|
done chan struct{} |
|
|
|
done chan struct{} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -87,7 +85,8 @@ func newProgram(args []string, stdin io.Reader) (*program, error) { |
|
|
|
conf: conf, |
|
|
|
conf: conf, |
|
|
|
paths: make(map[string]*path), |
|
|
|
paths: make(map[string]*path), |
|
|
|
clients: make(map[*client]struct{}), |
|
|
|
clients: make(map[*client]struct{}), |
|
|
|
udpClientsByAddr: make(map[udpClientAddr]*udpClient), |
|
|
|
udpPublishersMap: newUdpPublisherMap(), |
|
|
|
|
|
|
|
readersMap: newReadersMap(), |
|
|
|
metricsGather: make(chan metricsGatherReq), |
|
|
|
metricsGather: make(chan metricsGatherReq), |
|
|
|
clientNew: make(chan net.Conn), |
|
|
|
clientNew: make(chan net.Conn), |
|
|
|
clientClose: make(chan *client), |
|
|
|
clientClose: make(chan *client), |
|
|
|
@ -96,11 +95,8 @@ func newProgram(args []string, stdin io.Reader) (*program, error) { |
|
|
|
clientSetupPlay: make(chan clientSetupPlayReq), |
|
|
|
clientSetupPlay: make(chan clientSetupPlayReq), |
|
|
|
clientPlay: make(chan *client), |
|
|
|
clientPlay: make(chan *client), |
|
|
|
clientRecord: make(chan *client), |
|
|
|
clientRecord: make(chan *client), |
|
|
|
clientFrameUDP: make(chan clientFrameUDPReq), |
|
|
|
|
|
|
|
clientFrameTCP: make(chan clientFrameTCPReq), |
|
|
|
|
|
|
|
sourceReady: make(chan *source), |
|
|
|
sourceReady: make(chan *source), |
|
|
|
sourceNotReady: make(chan *source), |
|
|
|
sourceNotReady: make(chan *source), |
|
|
|
sourceFrame: make(chan sourceFrameReq), |
|
|
|
|
|
|
|
terminate: make(chan struct{}), |
|
|
|
terminate: make(chan struct{}), |
|
|
|
done: make(chan struct{}), |
|
|
|
done: make(chan struct{}), |
|
|
|
} |
|
|
|
} |
|
|
|
@ -280,6 +276,7 @@ outer: |
|
|
|
case client := <-p.clientPlay: |
|
|
|
case client := <-p.clientPlay: |
|
|
|
atomic.AddInt64(&p.countReader, 1) |
|
|
|
atomic.AddInt64(&p.countReader, 1) |
|
|
|
client.state = clientStatePlay |
|
|
|
client.state = clientStatePlay |
|
|
|
|
|
|
|
p.readersMap.add(client) |
|
|
|
|
|
|
|
|
|
|
|
case client := <-p.clientRecord: |
|
|
|
case client := <-p.clientRecord: |
|
|
|
atomic.AddInt64(&p.countPublisher, 1) |
|
|
|
atomic.AddInt64(&p.countPublisher, 1) |
|
|
|
@ -287,44 +284,24 @@ outer: |
|
|
|
|
|
|
|
|
|
|
|
if client.streamProtocol == gortsplib.StreamProtocolUDP { |
|
|
|
if client.streamProtocol == gortsplib.StreamProtocolUDP { |
|
|
|
for trackId, track := range client.streamTracks { |
|
|
|
for trackId, track := range client.streamTracks { |
|
|
|
key := makeUDPClientAddr(client.ip(), track.rtpPort) |
|
|
|
addr := makeUDPPublisherAddr(client.ip(), track.rtpPort) |
|
|
|
p.udpClientsByAddr[key] = &udpClient{ |
|
|
|
p.udpPublishersMap.add(addr, &udpPublisher{ |
|
|
|
client: client, |
|
|
|
client: client, |
|
|
|
trackId: trackId, |
|
|
|
trackId: trackId, |
|
|
|
streamType: gortsplib.StreamTypeRtp, |
|
|
|
streamType: gortsplib.StreamTypeRtp, |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
key = makeUDPClientAddr(client.ip(), track.rtcpPort) |
|
|
|
addr = makeUDPPublisherAddr(client.ip(), track.rtcpPort) |
|
|
|
p.udpClientsByAddr[key] = &udpClient{ |
|
|
|
p.udpPublishersMap.add(addr, &udpPublisher{ |
|
|
|
client: client, |
|
|
|
client: client, |
|
|
|
trackId: trackId, |
|
|
|
trackId: trackId, |
|
|
|
streamType: gortsplib.StreamTypeRtcp, |
|
|
|
streamType: gortsplib.StreamTypeRtcp, |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
client.path.onPublisherSetReady() |
|
|
|
client.path.onPublisherSetReady() |
|
|
|
|
|
|
|
|
|
|
|
case req := <-p.clientFrameUDP: |
|
|
|
|
|
|
|
pub, ok := p.udpClientsByAddr[makeUDPClientAddr(req.addr.IP, req.addr.Port)] |
|
|
|
|
|
|
|
if !ok { |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// client sent RTP on RTCP port or vice-versa
|
|
|
|
|
|
|
|
if pub.streamType != req.streamType { |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
atomic.StoreInt64(pub.client.udpLastFrameTimes[pub.trackId], time.Now().Unix()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub.client.rtcpReceivers[pub.trackId].OnFrame(req.streamType, req.buf) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
p.forwardFrame(pub.client.path, pub.trackId, req.streamType, req.buf) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case req := <-p.clientFrameTCP: |
|
|
|
|
|
|
|
p.forwardFrame(req.path, req.trackId, req.streamType, req.buf) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case source := <-p.sourceReady: |
|
|
|
case source := <-p.sourceReady: |
|
|
|
source.path.log("source ready") |
|
|
|
source.path.log("source ready") |
|
|
|
source.path.onPublisherSetReady() |
|
|
|
source.path.onPublisherSetReady() |
|
|
|
@ -333,9 +310,6 @@ outer: |
|
|
|
source.path.log("source not ready") |
|
|
|
source.path.log("source not ready") |
|
|
|
source.path.onPublisherSetNotReady() |
|
|
|
source.path.onPublisherSetNotReady() |
|
|
|
|
|
|
|
|
|
|
|
case req := <-p.sourceFrame: |
|
|
|
|
|
|
|
p.forwardFrame(req.source.path, req.trackId, req.streamType, req.buf) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case <-p.terminate: |
|
|
|
case <-p.terminate: |
|
|
|
break outer |
|
|
|
break outer |
|
|
|
} |
|
|
|
} |
|
|
|
@ -362,11 +336,8 @@ outer: |
|
|
|
|
|
|
|
|
|
|
|
case <-p.clientPlay: |
|
|
|
case <-p.clientPlay: |
|
|
|
case <-p.clientRecord: |
|
|
|
case <-p.clientRecord: |
|
|
|
case <-p.clientFrameUDP: |
|
|
|
|
|
|
|
case <-p.clientFrameTCP: |
|
|
|
|
|
|
|
case <-p.sourceReady: |
|
|
|
case <-p.sourceReady: |
|
|
|
case <-p.sourceNotReady: |
|
|
|
case <-p.sourceNotReady: |
|
|
|
case <-p.sourceFrame: |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}() |
|
|
|
}() |
|
|
|
@ -410,11 +381,8 @@ outer: |
|
|
|
close(p.clientSetupPlay) |
|
|
|
close(p.clientSetupPlay) |
|
|
|
close(p.clientPlay) |
|
|
|
close(p.clientPlay) |
|
|
|
close(p.clientRecord) |
|
|
|
close(p.clientRecord) |
|
|
|
close(p.clientFrameUDP) |
|
|
|
|
|
|
|
close(p.clientFrameTCP) |
|
|
|
|
|
|
|
close(p.sourceReady) |
|
|
|
close(p.sourceReady) |
|
|
|
close(p.sourceNotReady) |
|
|
|
close(p.sourceNotReady) |
|
|
|
close(p.sourceFrame) |
|
|
|
|
|
|
|
close(p.done) |
|
|
|
close(p.done) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -435,44 +403,6 @@ func (p *program) findConfForPathName(name string) *confPath { |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *program) forwardFrame(path *path, trackId int, streamType gortsplib.StreamType, frame []byte) { |
|
|
|
|
|
|
|
for c := range p.clients { |
|
|
|
|
|
|
|
if c.path != path || |
|
|
|
|
|
|
|
c.state != clientStatePlay { |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
track, ok := c.streamTracks[trackId] |
|
|
|
|
|
|
|
if !ok { |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if c.streamProtocol == gortsplib.StreamProtocolUDP { |
|
|
|
|
|
|
|
if streamType == gortsplib.StreamTypeRtp { |
|
|
|
|
|
|
|
p.serverRtp.write(frame, &net.UDPAddr{ |
|
|
|
|
|
|
|
IP: c.ip(), |
|
|
|
|
|
|
|
Zone: c.zone(), |
|
|
|
|
|
|
|
Port: track.rtpPort, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
p.serverRtcp.write(frame, &net.UDPAddr{ |
|
|
|
|
|
|
|
IP: c.ip(), |
|
|
|
|
|
|
|
Zone: c.zone(), |
|
|
|
|
|
|
|
Port: track.rtcpPort, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
c.tcpFrame <- &gortsplib.InterleavedFrame{ |
|
|
|
|
|
|
|
TrackId: trackId, |
|
|
|
|
|
|
|
StreamType: streamType, |
|
|
|
|
|
|
|
Content: frame, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
func main() { |
|
|
|
_, err := newProgram(os.Args[1:], os.Stdin) |
|
|
|
_, err := newProgram(os.Args[1:], os.Stdin) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
|