2019-12-28 21:07:03 +00:00
package main
import (
"fmt"
2020-06-27 15:23:43 +00:00
"io"
2019-12-28 21:07:03 +00:00
"log"
2020-06-15 20:41:14 +00:00
"net"
2020-06-27 13:42:54 +00:00
"net/http"
_ "net/http/pprof"
2019-12-28 21:07:03 +00:00
"os"
2020-07-31 16:12:42 +00:00
"os/exec"
2020-07-29 21:30:42 +00:00
"time"
2019-12-28 21:07:03 +00:00
2020-07-12 10:34:35 +00:00
"github.com/aler9/gortsplib"
2020-07-19 09:51:28 +00:00
"github.com/aler9/sdp/v3"
2019-12-28 21:07:03 +00:00
"gopkg.in/alecthomas/kingpin.v2"
)
2020-06-26 09:00:10 +00:00
var Version = "v0.0.0"
2019-12-28 21:07:03 +00:00
2020-07-30 15:30:50 +00:00
const (
pprofAddress = ":9999"
)
2020-07-30 17:21:36 +00:00
type logDestination int
const (
logDestinationStdout logDestination = iota
logDestinationFile
)
2020-06-27 11:38:35 +00:00
type programEvent interface {
isProgramEvent ( )
}
2020-07-30 15:30:50 +00:00
type programEventMetrics struct {
res chan * metricsData
}
func ( programEventMetrics ) isProgramEvent ( ) { }
2020-06-27 11:38:35 +00:00
type programEventClientNew struct {
nconn net . Conn
}
func ( programEventClientNew ) isProgramEvent ( ) { }
type programEventClientClose struct {
done chan struct { }
2020-07-29 21:30:42 +00:00
client * client
2020-06-27 11:38:35 +00:00
}
func ( programEventClientClose ) isProgramEvent ( ) { }
2020-06-30 13:12:39 +00:00
type programEventClientDescribe struct {
2020-07-29 21:30:42 +00:00
client * client
path string
2020-06-27 11:38:35 +00:00
}
2020-06-30 13:12:39 +00:00
func ( programEventClientDescribe ) isProgramEvent ( ) { }
2020-06-27 11:38:35 +00:00
type programEventClientAnnounce struct {
2020-07-29 21:30:42 +00:00
res chan error
client * client
path string
sdpText [ ] byte
sdpParsed * sdp . SessionDescription
2020-06-27 11:38:35 +00:00
}
func ( programEventClientAnnounce ) isProgramEvent ( ) { }
type programEventClientSetupPlay struct {
2020-08-03 15:35:34 +00:00
res chan error
client * client
path string
trackId int
2020-06-27 11:38:35 +00:00
}
func ( programEventClientSetupPlay ) isProgramEvent ( ) { }
type programEventClientSetupRecord struct {
2020-08-03 15:35:34 +00:00
res chan error
client * client
2020-06-27 11:38:35 +00:00
}
func ( programEventClientSetupRecord ) isProgramEvent ( ) { }
type programEventClientPlay1 struct {
res chan error
2020-07-29 21:30:42 +00:00
client * client
2020-06-27 11:38:35 +00:00
}
func ( programEventClientPlay1 ) isProgramEvent ( ) { }
type programEventClientPlay2 struct {
2020-07-13 09:12:20 +00:00
done chan struct { }
2020-07-29 21:30:42 +00:00
client * client
2020-06-27 11:38:35 +00:00
}
func ( programEventClientPlay2 ) isProgramEvent ( ) { }
2020-07-13 09:12:20 +00:00
type programEventClientPlayStop struct {
done chan struct { }
2020-07-29 21:30:42 +00:00
client * client
2020-07-13 09:12:20 +00:00
}
func ( programEventClientPlayStop ) isProgramEvent ( ) { }
2020-06-27 11:38:35 +00:00
type programEventClientRecord struct {
2020-07-13 09:12:20 +00:00
done chan struct { }
2020-07-29 21:30:42 +00:00
client * client
2020-06-27 11:38:35 +00:00
}
func ( programEventClientRecord ) isProgramEvent ( ) { }
2020-07-13 09:12:20 +00:00
type programEventClientRecordStop struct {
done chan struct { }
2020-07-29 21:30:42 +00:00
client * client
2020-07-13 09:12:20 +00:00
}
func ( programEventClientRecordStop ) isProgramEvent ( ) { }
2020-06-30 13:12:39 +00:00
type programEventClientFrameUdp struct {
2020-07-12 20:53:22 +00:00
addr * net . UDPAddr
streamType gortsplib . StreamType
buf [ ] byte
2020-06-27 11:38:35 +00:00
}
2020-06-30 13:12:39 +00:00
func ( programEventClientFrameUdp ) isProgramEvent ( ) { }
2020-06-27 11:38:35 +00:00
2020-06-30 13:12:39 +00:00
type programEventClientFrameTcp struct {
2020-07-12 20:53:22 +00:00
path string
trackId int
streamType gortsplib . StreamType
buf [ ] byte
2020-06-27 11:38:35 +00:00
}
2020-06-30 13:12:39 +00:00
func ( programEventClientFrameTcp ) isProgramEvent ( ) { }
2020-07-29 21:30:42 +00:00
type programEventSourceReady struct {
2020-07-14 07:19:07 +00:00
source * source
2020-06-30 13:12:39 +00:00
}
2020-07-29 21:30:42 +00:00
func ( programEventSourceReady ) isProgramEvent ( ) { }
2020-06-30 13:12:39 +00:00
2020-07-29 21:30:42 +00:00
type programEventSourceNotReady struct {
2020-07-14 07:19:07 +00:00
source * source
2020-06-30 13:12:39 +00:00
}
2020-07-29 21:30:42 +00:00
func ( programEventSourceNotReady ) isProgramEvent ( ) { }
2020-06-30 13:12:39 +00:00
2020-07-29 21:30:42 +00:00
type programEventSourceFrame struct {
2020-07-14 07:19:07 +00:00
source * source
2020-07-12 20:53:22 +00:00
trackId int
streamType gortsplib . StreamType
buf [ ] byte
2020-06-30 13:12:39 +00:00
}
2020-07-29 21:30:42 +00:00
func ( programEventSourceFrame ) isProgramEvent ( ) { }
2020-06-27 11:38:35 +00:00
type programEventTerminate struct { }
func ( programEventTerminate ) isProgramEvent ( ) { }
2020-05-10 13:33:42 +00:00
type program struct {
2020-08-03 15:54:23 +00:00
conf * conf
logFile * os . File
metrics * metrics
serverRtsp * serverTcp
serverRtp * serverUdp
serverRtcp * serverUdp
sources [ ] * source
clients map [ * client ] struct { }
udpClientPublishers map [ ipKey ] * client
paths map [ string ] * path
cmds [ ] * exec . Cmd
publisherCount int
readerCount int
2020-06-27 11:38:35 +00:00
events chan programEvent
done chan struct { }
2020-05-10 13:33:42 +00:00
}
2020-02-16 15:05:08 +00:00
2020-07-20 09:42:56 +00:00
func newProgram ( args [ ] string , stdin io . Reader ) ( * program , error ) {
2020-06-27 19:22:50 +00:00
k := kingpin . New ( "rtsp-simple-server" ,
"rtsp-simple-server " + Version + "\n\nRTSP server." )
2020-05-11 07:31:56 +00:00
2020-06-27 19:22:50 +00:00
argVersion := k . Flag ( "version" , "print version" ) . Bool ( )
2020-07-13 15:27:04 +00:00
argConfPath := k . Arg ( "confpath" , "path to a config file. The default is rtsp-simple-server.yml. Use 'stdin' to read config from stdin" ) . Default ( "rtsp-simple-server.yml" ) . String ( )
2020-05-11 07:31:56 +00:00
2020-07-20 09:42:56 +00:00
kingpin . MustParse ( k . Parse ( args ) )
2020-05-11 07:31:56 +00:00
2020-06-27 15:23:43 +00:00
if * argVersion == true {
2020-05-11 07:39:49 +00:00
fmt . Println ( Version )
2020-05-10 19:32:40 +00:00
os . Exit ( 0 )
2020-01-26 11:58:56 +00:00
}
2020-06-27 15:23:43 +00:00
conf , err := loadConf ( * argConfPath , stdin )
if err != nil {
return nil , err
}
2020-06-30 13:12:39 +00:00
p := & program {
2020-08-03 15:54:23 +00:00
conf : conf ,
clients : make ( map [ * client ] struct { } ) ,
udpClientPublishers : make ( map [ ipKey ] * client ) ,
paths : make ( map [ string ] * path ) ,
events : make ( chan programEvent ) ,
done : make ( chan struct { } ) ,
2020-06-30 13:12:39 +00:00
}
2020-07-30 17:21:36 +00:00
if _ , ok := p . conf . logDestinationsParsed [ logDestinationFile ] ; ok {
p . logFile , err = os . OpenFile ( p . conf . LogFile , os . O_APPEND | os . O_CREATE | os . O_WRONLY , 0644 )
if err != nil {
log . Fatal ( "ERR:" , err )
}
}
p . log ( "rtsp-simple-server %s" , Version )
2020-07-30 11:31:18 +00:00
for path , confp := range conf . Paths {
if path == "all" {
continue
}
2020-07-30 15:30:50 +00:00
p . paths [ path ] = newPath ( p , path , confp , true )
2020-07-30 11:31:18 +00:00
if confp . Source != "record" {
s := newSource ( p , path , confp )
2020-07-14 07:19:07 +00:00
p . sources = append ( p . sources , s )
2020-07-30 11:31:18 +00:00
p . paths [ path ] . publisher = s
2020-06-30 13:12:39 +00:00
}
2019-12-28 21:07:03 +00:00
}
2020-07-30 15:30:50 +00:00
if conf . Metrics {
p . metrics = newMetrics ( p )
}
2020-06-27 15:23:43 +00:00
if conf . Pprof {
2020-06-27 13:42:54 +00:00
go func ( mux * http . ServeMux ) {
2020-07-30 15:30:50 +00:00
p . log ( "[pprof] opened on " + pprofAddress )
panic ( ( & http . Server {
Addr : pprofAddress ,
2020-06-27 13:42:54 +00:00
Handler : mux ,
2020-07-30 15:30:50 +00:00
} ) . ListenAndServe ( ) )
2020-06-27 13:42:54 +00:00
} ( http . DefaultServeMux )
http . DefaultServeMux = http . NewServeMux ( )
}
2020-07-30 15:30:50 +00:00
p . serverRtp , err = newServerUdp ( p , conf . RtpPort , gortsplib . StreamTypeRtp )
2019-12-28 21:07:03 +00:00
if err != nil {
return nil , err
}
2020-07-30 15:30:50 +00:00
p . serverRtcp , err = newServerUdp ( p , conf . RtcpPort , gortsplib . StreamTypeRtcp )
2019-12-28 21:07:03 +00:00
if err != nil {
return nil , err
}
2020-07-30 15:30:50 +00:00
p . serverRtsp , err = newServerTcp ( p )
2019-12-28 21:07:03 +00:00
if err != nil {
return nil , err
}
2020-07-31 16:12:42 +00:00
for _ , confp := range conf . Paths {
if confp . RunOnInit != "" {
onInitCmd := exec . Command ( "/bin/sh" , "-c" , confp . RunOnInit )
onInitCmd . Stdout = os . Stdout
onInitCmd . Stderr = os . Stderr
err := onInitCmd . Start ( )
if err != nil {
p . log ( "ERR: %s" , err )
}
p . cmds = append ( p . cmds , onInitCmd )
}
}
2020-07-30 15:30:50 +00:00
if p . metrics != nil {
go p . metrics . run ( )
}
go p . serverRtp . run ( )
go p . serverRtcp . run ( )
go p . serverRtsp . run ( )
2020-07-14 07:19:07 +00:00
for _ , s := range p . sources {
2020-06-30 13:12:39 +00:00
go s . run ( )
}
2020-06-27 11:38:35 +00:00
go p . run ( )
2019-12-28 21:07:03 +00:00
2020-05-10 13:33:42 +00:00
return p , nil
2019-12-28 21:07:03 +00:00
}
2020-06-27 12:18:16 +00:00
func ( p * program ) log ( format string , args ... interface { } ) {
2020-07-30 17:21:36 +00:00
line := fmt . Sprintf ( "[%d/%d/%d] " + format , append ( [ ] interface { } { len ( p . clients ) ,
2020-07-20 09:42:56 +00:00
p . publisherCount , p . readerCount } , args ... ) ... )
2020-07-30 17:21:36 +00:00
if _ , ok := p . conf . logDestinationsParsed [ logDestinationStdout ] ; ok {
log . Println ( line )
}
if _ , ok := p . conf . logDestinationsParsed [ logDestinationFile ] ; ok {
p . logFile . WriteString ( line + "\n" )
}
2020-06-27 12:18:16 +00:00
}
2020-06-27 11:38:35 +00:00
func ( p * program ) run ( ) {
2020-07-29 21:30:42 +00:00
checkPathsTicker := time . NewTicker ( 5 * time . Second )
defer checkPathsTicker . Stop ( )
2020-06-27 11:38:35 +00:00
outer :
2020-07-29 21:30:42 +00:00
for {
select {
case <- checkPathsTicker . C :
for _ , path := range p . paths {
path . check ( )
2020-06-27 11:38:35 +00:00
}
2020-07-29 21:30:42 +00:00
case rawEvt := <- p . events :
switch evt := rawEvt . ( type ) {
2020-07-30 15:30:50 +00:00
case programEventMetrics :
evt . res <- & metricsData {
clientCount : len ( p . clients ) ,
publisherCount : p . publisherCount ,
readerCount : p . readerCount ,
}
2020-07-29 21:30:42 +00:00
case programEventClientNew :
2020-07-30 11:31:18 +00:00
c := newClient ( p , evt . nconn )
2020-07-29 21:30:42 +00:00
p . clients [ c ] = struct { } { }
c . log ( "connected" )
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientClose :
delete ( p . clients , evt . client )
2020-07-30 11:31:18 +00:00
if evt . client . pathId != "" {
if path , ok := p . paths [ evt . client . pathId ] ; ok {
2020-07-29 21:30:42 +00:00
if path . publisher == evt . client {
2020-07-30 11:31:18 +00:00
path . publisherRemove ( )
2020-07-29 21:30:42 +00:00
2020-07-30 11:31:18 +00:00
if ! path . permanent {
delete ( p . paths , evt . client . pathId )
}
2020-07-29 21:30:42 +00:00
}
}
2020-06-27 11:38:35 +00:00
}
2020-07-29 21:30:42 +00:00
evt . client . log ( "disconnected" )
close ( evt . done )
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientDescribe :
path , ok := p . paths [ evt . path ]
if ! ok {
2020-07-30 11:31:18 +00:00
evt . client . describeRes <- describeRes { nil , fmt . Errorf ( "no one is publishing on path '%s'" , evt . path ) }
2020-07-29 21:30:42 +00:00
continue
}
2020-06-27 11:38:35 +00:00
2020-07-30 11:31:18 +00:00
path . describe ( evt . client )
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientAnnounce :
2020-07-30 11:31:18 +00:00
if path , ok := p . paths [ evt . path ] ; ok {
if path . publisher != nil {
evt . res <- fmt . Errorf ( "someone is already publishing on path '%s'" , evt . path )
continue
}
} else {
2020-07-30 15:30:50 +00:00
p . paths [ evt . path ] = newPath ( p , evt . path , p . findConfForPath ( evt . path ) , false )
2020-07-29 21:30:42 +00:00
}
2020-06-30 13:12:39 +00:00
2020-07-30 11:31:18 +00:00
p . paths [ evt . path ] . publisher = evt . client
2020-07-29 21:30:42 +00:00
p . paths [ evt . path ] . publisherSdpText = evt . sdpText
p . paths [ evt . path ] . publisherSdpParsed = evt . sdpParsed
2020-07-30 11:31:18 +00:00
evt . client . pathId = evt . path
evt . client . state = clientStateAnnounce
2020-07-29 21:30:42 +00:00
evt . res <- nil
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientSetupPlay :
path , ok := p . paths [ evt . path ]
if ! ok || ! path . publisherReady {
evt . res <- fmt . Errorf ( "no one is publishing on path '%s'" , evt . path )
continue
}
2020-06-27 11:38:35 +00:00
2020-08-03 15:35:34 +00:00
if evt . trackId >= len ( path . publisherSdpParsed . MediaDescriptions ) {
evt . res <- fmt . Errorf ( "track %d does not exist" , evt . trackId )
2020-07-29 21:30:42 +00:00
continue
}
2020-06-30 13:12:39 +00:00
2020-07-30 11:31:18 +00:00
evt . client . pathId = evt . path
2020-07-29 21:30:42 +00:00
evt . client . state = clientStatePrePlay
evt . res <- nil
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientSetupRecord :
evt . client . state = clientStatePreRecord
evt . res <- nil
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientPlay1 :
2020-07-30 11:31:18 +00:00
path , ok := p . paths [ evt . client . pathId ]
2020-07-29 21:30:42 +00:00
if ! ok || ! path . publisherReady {
2020-07-30 11:31:18 +00:00
evt . res <- fmt . Errorf ( "no one is publishing on path '%s'" , evt . client . pathId )
2020-07-29 21:30:42 +00:00
continue
}
2020-07-13 09:12:20 +00:00
2020-08-03 15:35:34 +00:00
if len ( evt . client . streamTracks ) == 0 {
evt . res <- fmt . Errorf ( "no tracks have been setup" )
2020-07-29 21:30:42 +00:00
continue
}
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
evt . res <- nil
2020-07-13 09:12:20 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientPlay2 :
p . readerCount += 1
evt . client . state = clientStatePlay
close ( evt . done )
2020-07-13 09:12:20 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientPlayStop :
p . readerCount -= 1
evt . client . state = clientStatePrePlay
close ( evt . done )
case programEventClientRecord :
p . publisherCount += 1
evt . client . state = clientStateRecord
2020-08-05 08:18:38 +00:00
if evt . client . streamProtocol == gortsplib . StreamProtocolUdp {
p . udpClientPublishers [ makeIpKey ( evt . client . ip ( ) ) ] = evt . client
}
2020-07-30 11:31:18 +00:00
p . paths [ evt . client . pathId ] . publisherSetReady ( )
2020-07-29 21:30:42 +00:00
close ( evt . done )
2020-07-13 09:12:20 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientRecordStop :
p . publisherCount -= 1
evt . client . state = clientStatePreRecord
2020-08-05 08:18:38 +00:00
if evt . client . streamProtocol == gortsplib . StreamProtocolUdp {
delete ( p . udpClientPublishers , makeIpKey ( evt . client . ip ( ) ) )
}
2020-07-30 11:31:18 +00:00
p . paths [ evt . client . pathId ] . publisherSetNotReady ( )
2020-07-29 21:30:42 +00:00
close ( evt . done )
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientFrameUdp :
2020-08-03 15:54:23 +00:00
client , trackId := p . findUdpClientPublisher ( evt . addr , evt . streamType )
2020-07-29 21:30:42 +00:00
if client == nil {
continue
}
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
client . rtcpReceivers [ trackId ] . OnFrame ( evt . streamType , evt . buf )
2020-07-30 11:31:18 +00:00
p . forwardFrame ( client . pathId , trackId , evt . streamType , evt . buf )
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
case programEventClientFrameTcp :
p . forwardFrame ( evt . path , evt . trackId , evt . streamType , evt . buf )
2020-06-27 11:38:35 +00:00
2020-07-29 21:30:42 +00:00
case programEventSourceReady :
evt . source . log ( "ready" )
2020-07-30 11:31:18 +00:00
p . paths [ evt . source . pathId ] . publisherSetReady ( )
2020-06-30 13:12:39 +00:00
2020-07-29 21:30:42 +00:00
case programEventSourceNotReady :
evt . source . log ( "not ready" )
2020-07-30 11:31:18 +00:00
p . paths [ evt . source . pathId ] . publisherSetNotReady ( )
2020-06-30 13:12:39 +00:00
2020-07-29 21:30:42 +00:00
case programEventSourceFrame :
2020-07-30 11:31:18 +00:00
p . forwardFrame ( evt . source . pathId , evt . trackId , evt . streamType , evt . buf )
2020-06-30 13:12:39 +00:00
2020-07-29 21:30:42 +00:00
case programEventTerminate :
break outer
}
2020-06-27 11:38:35 +00:00
}
}
go func ( ) {
for rawEvt := range p . events {
switch evt := rawEvt . ( type ) {
2020-07-30 15:30:50 +00:00
case programEventMetrics :
evt . res <- nil
2020-06-27 11:38:35 +00:00
case programEventClientClose :
close ( evt . done )
2020-06-30 13:12:39 +00:00
case programEventClientDescribe :
2020-07-30 11:31:18 +00:00
evt . client . describeRes <- describeRes { nil , fmt . Errorf ( "terminated" ) }
2020-06-27 11:38:35 +00:00
case programEventClientAnnounce :
evt . res <- fmt . Errorf ( "terminated" )
case programEventClientSetupPlay :
evt . res <- fmt . Errorf ( "terminated" )
case programEventClientSetupRecord :
evt . res <- fmt . Errorf ( "terminated" )
case programEventClientPlay1 :
evt . res <- fmt . Errorf ( "terminated" )
case programEventClientPlay2 :
2020-07-13 09:12:20 +00:00
close ( evt . done )
case programEventClientPlayStop :
close ( evt . done )
2020-06-27 11:38:35 +00:00
case programEventClientRecord :
2020-07-13 09:12:20 +00:00
close ( evt . done )
case programEventClientRecordStop :
close ( evt . done )
2020-06-27 11:38:35 +00:00
}
}
} ( )
2020-07-31 16:12:42 +00:00
for _ , cmd := range p . cmds {
cmd . Process . Signal ( os . Interrupt )
cmd . Wait ( )
}
2020-07-14 07:19:07 +00:00
for _ , s := range p . sources {
2020-07-29 21:30:42 +00:00
s . events <- sourceEventTerminate { }
<- s . done
2020-06-30 13:12:39 +00:00
}
2020-07-30 15:30:50 +00:00
p . serverRtsp . close ( )
p . serverRtcp . close ( )
p . serverRtp . close ( )
2020-06-27 11:38:35 +00:00
for c := range p . clients {
2020-07-29 21:30:42 +00:00
c . conn . NetConn ( ) . Close ( )
<- c . done
2020-06-27 11:38:35 +00:00
}
2020-07-30 15:30:50 +00:00
if p . metrics != nil {
p . metrics . close ( )
}
2020-07-30 17:21:36 +00:00
if p . logFile != nil {
p . logFile . Close ( )
}
2020-06-27 11:38:35 +00:00
close ( p . events )
close ( p . done )
}
func ( p * program ) close ( ) {
p . events <- programEventTerminate { }
<- p . done
}
2020-07-29 21:30:42 +00:00
func ( p * program ) findConfForPath ( path string ) * confPath {
2020-07-30 11:31:18 +00:00
if confp , ok := p . conf . Paths [ path ] ; ok {
return confp
2020-07-29 21:30:42 +00:00
}
2020-07-30 11:31:18 +00:00
if confp , ok := p . conf . Paths [ "all" ] ; ok {
return confp
2020-07-29 21:30:42 +00:00
}
return nil
}
2020-08-03 15:54:23 +00:00
func ( p * program ) findUdpClientPublisher ( addr * net . UDPAddr , streamType gortsplib . StreamType ) ( * client , int ) {
c , ok := p . udpClientPublishers [ makeIpKey ( addr . IP ) ]
if ok {
for i , t := range c . streamTracks {
2020-07-12 20:53:22 +00:00
if streamType == gortsplib . StreamTypeRtp {
2020-07-11 14:40:19 +00:00
if t . rtpPort == addr . Port {
2020-08-03 15:54:23 +00:00
return c , i
2020-07-11 14:40:19 +00:00
}
} else {
if t . rtcpPort == addr . Port {
2020-08-03 15:54:23 +00:00
return c , i
2020-07-11 14:40:19 +00:00
}
}
}
}
2020-08-03 15:54:23 +00:00
2020-07-11 14:40:19 +00:00
return nil , - 1
}
2020-07-12 20:53:22 +00:00
func ( p * program ) forwardFrame ( path string , trackId int , streamType gortsplib . StreamType , frame [ ] byte ) {
2020-07-30 11:31:18 +00:00
for c := range p . clients {
2020-08-03 15:35:34 +00:00
if c . pathId != path ||
c . state != clientStatePlay {
continue
}
2020-06-27 11:38:35 +00:00
2020-08-03 15:35:34 +00:00
track , ok := c . streamTracks [ trackId ]
if ! ok {
continue
}
if c . streamProtocol == gortsplib . StreamProtocolUdp {
if streamType == gortsplib . StreamTypeRtp {
p . serverRtp . write ( & udpAddrBufPair {
addr : & net . UDPAddr {
IP : c . ip ( ) ,
Zone : c . zone ( ) ,
Port : track . rtpPort ,
} ,
buf : frame ,
} )
2020-06-27 11:38:35 +00:00
} else {
2020-08-03 15:35:34 +00:00
p . serverRtcp . write ( & udpAddrBufPair {
addr : & net . UDPAddr {
IP : c . ip ( ) ,
Zone : c . zone ( ) ,
Port : track . rtcpPort ,
2020-07-12 10:34:35 +00:00
} ,
2020-08-03 15:35:34 +00:00
buf : frame ,
} )
}
} else {
buf := c . writeBuf . swap ( )
buf = buf [ : len ( frame ) ]
copy ( buf , frame )
c . events <- clientEventFrameTcp {
frame : & gortsplib . InterleavedFrame {
TrackId : trackId ,
StreamType : streamType ,
Content : buf ,
} ,
2020-06-27 11:38:35 +00:00
}
}
}
2020-05-10 14:33:20 +00:00
}
2019-12-28 21:07:03 +00:00
func main ( ) {
2020-06-27 15:23:43 +00:00
_ , err := newProgram ( os . Args [ 1 : ] , os . Stdin )
2019-12-28 21:07:03 +00:00
if err != nil {
2020-07-28 13:10:33 +00:00
log . Fatal ( "ERR:" , err )
2019-12-28 21:07:03 +00:00
}
2020-06-24 20:42:39 +00:00
select { }
2019-12-28 21:07:03 +00:00
}