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"
2019-12-28 21:07:03 +00:00
"os"
2020-09-03 14:24:39 +00:00
"sync/atomic"
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"
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-08-30 13:27:03 +00:00
const (
checkPathPeriod = 5 * time . Second
)
2020-07-30 17:21:36 +00:00
type logDestination int
const (
logDestinationStdout logDestination = iota
logDestinationFile
)
2020-09-14 11:41:10 +00:00
type logWriter func ( [ ] byte ) ( int , error )
func ( f logWriter ) Write ( p [ ] byte ) ( int , error ) {
return f ( p )
}
2020-05-10 13:33:42 +00:00
type program struct {
2020-08-05 12:59:38 +00:00
conf * conf
logFile * os . File
metrics * metrics
2020-08-30 12:10:05 +00:00
pprof * pprof
2020-08-30 12:15:00 +00:00
paths map [ string ] * path
2020-09-05 11:19:55 +00:00
serverRtp * serverUDP
serverRtcp * serverUDP
serverRtsp * serverTCP
2020-08-05 12:59:38 +00:00
clients map [ * client ] struct { }
2020-09-18 22:08:05 +00:00
udpPublishersMap * udpPublishersMap
readersMap * readersMap
2020-09-03 14:24:39 +00:00
countClient int64
countPublisher int64
countReader int64
2020-06-27 11:38:35 +00:00
2020-08-31 22:01:17 +00:00
metricsGather chan metricsGatherReq
clientNew chan net . Conn
clientClose chan * client
clientDescribe chan clientDescribeReq
clientAnnounce chan clientAnnounceReq
clientSetupPlay chan clientSetupPlayReq
clientPlay chan * client
clientRecord chan * client
sourceReady chan * source
sourceNotReady chan * source
terminate chan struct { }
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-05 12:59:38 +00:00
conf : conf ,
2020-08-30 12:15:00 +00:00
paths : make ( map [ string ] * path ) ,
2020-08-05 12:59:38 +00:00
clients : make ( map [ * client ] struct { } ) ,
2020-09-18 22:08:05 +00:00
udpPublishersMap : newUdpPublisherMap ( ) ,
readersMap : newReadersMap ( ) ,
2020-08-31 22:01:17 +00:00
metricsGather : make ( chan metricsGatherReq ) ,
clientNew : make ( chan net . Conn ) ,
clientClose : make ( chan * client ) ,
clientDescribe : make ( chan clientDescribeReq ) ,
clientAnnounce : make ( chan clientAnnounceReq ) ,
clientSetupPlay : make ( chan clientSetupPlayReq ) ,
clientPlay : make ( chan * client ) ,
clientRecord : make ( chan * client ) ,
sourceReady : make ( chan * source ) ,
sourceNotReady : make ( chan * source ) ,
terminate : make ( chan struct { } ) ,
2020-08-05 12:59:38 +00:00
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 {
2020-08-30 11:44:15 +00:00
return nil , err
2020-07-30 17:21:36 +00:00
}
}
2020-09-14 11:41:10 +00:00
log . SetOutput ( logWriter ( p . logOutput ) )
2020-07-30 17:21:36 +00:00
p . log ( "rtsp-simple-server %s" , Version )
2020-07-30 15:30:50 +00:00
if conf . Metrics {
2020-08-30 11:44:15 +00:00
p . metrics , err = newMetrics ( p )
if err != nil {
return nil , err
}
2020-07-30 15:30:50 +00:00
}
2020-06-27 15:23:43 +00:00
if conf . Pprof {
2020-08-30 12:10:05 +00:00
p . pprof , err = newPprof ( p )
if err != nil {
return nil , err
}
2020-06-27 13:42:54 +00:00
}
2020-08-30 12:15:00 +00:00
for name , confp := range conf . Paths {
if name == "all" {
continue
}
p . paths [ name ] = newPath ( p , name , confp , true )
}
2020-09-05 11:19:55 +00:00
if _ , ok := conf . protocolsParsed [ gortsplib . StreamProtocolUDP ] ; ok {
p . serverRtp , err = newServerUDP ( p , conf . RtpPort , gortsplib . StreamTypeRtp )
2020-08-17 16:22:14 +00:00
if err != nil {
return nil , err
}
2019-12-28 21:07:03 +00:00
2020-09-05 11:19:55 +00:00
p . serverRtcp , err = newServerUDP ( p , conf . RtcpPort , gortsplib . StreamTypeRtcp )
2020-08-17 16:22:14 +00:00
if err != nil {
return nil , err
}
2019-12-28 21:07:03 +00:00
}
2020-09-05 11:19:55 +00:00
p . serverRtsp , err = newServerTCP ( p )
2020-08-29 17:48:41 +00:00
if err != nil {
return nil , err
}
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-09-03 14:24:39 +00:00
countClient := atomic . LoadInt64 ( & p . countClient )
countPublisher := atomic . LoadInt64 ( & p . countPublisher )
countReader := atomic . LoadInt64 ( & p . countReader )
2020-09-14 11:41:10 +00:00
log . Printf ( fmt . Sprintf ( "[%d/%d/%d] " + format , append ( [ ] interface { } { countClient ,
countPublisher , countReader } , args ... ) ... ) )
}
2020-07-30 17:21:36 +00:00
2020-09-14 11:41:10 +00:00
func ( p * program ) logOutput ( line [ ] byte ) ( int , error ) {
2020-07-30 17:21:36 +00:00
if _ , ok := p . conf . logDestinationsParsed [ logDestinationStdout ] ; ok {
2020-09-14 11:41:10 +00:00
print ( string ( line ) )
2020-07-30 17:21:36 +00:00
}
2020-08-30 13:27:03 +00:00
2020-07-30 17:21:36 +00:00
if _ , ok := p . conf . logDestinationsParsed [ logDestinationFile ] ; ok {
2020-09-14 11:41:10 +00:00
p . logFile . Write ( line )
2020-07-30 17:21:36 +00:00
}
2020-09-14 11:41:10 +00:00
return len ( line ) , nil
2020-06-27 12:18:16 +00:00
}
2020-06-27 11:38:35 +00:00
func ( p * program ) run ( ) {
2020-08-30 12:27:26 +00:00
if p . metrics != nil {
go p . metrics . run ( )
}
if p . pprof != nil {
go p . pprof . run ( )
}
if p . serverRtp != nil {
go p . serverRtp . run ( )
}
if p . serverRtcp != nil {
go p . serverRtcp . run ( )
}
go p . serverRtsp . run ( )
for _ , p := range p . paths {
p . onInit ( )
}
2020-08-30 13:27:03 +00:00
checkPathsTicker := time . NewTicker ( checkPathPeriod )
2020-07-29 21:30:42 +00:00
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 {
2020-08-30 11:18:43 +00:00
path . onCheck ( )
2020-06-27 11:38:35 +00:00
}
2020-08-31 22:01:17 +00:00
case req := <- p . metricsGather :
req . res <- & metricsData {
2020-09-03 14:24:39 +00:00
countClient : p . countClient ,
countPublisher : p . countPublisher ,
countReader : p . countReader ,
2020-08-31 22:01:17 +00:00
}
2020-07-30 15:30:50 +00:00
2020-08-31 22:01:17 +00:00
case conn := <- p . clientNew :
c := newClient ( p , conn )
p . clients [ c ] = struct { } { }
2020-09-03 14:24:39 +00:00
atomic . AddInt64 ( & p . countClient , 1 )
2020-08-31 22:01:17 +00:00
c . log ( "connected" )
2020-06-27 11:38:35 +00:00
2020-08-31 22:01:17 +00:00
case client := <- p . clientClose :
if _ , ok := p . clients [ client ] ; ! ok {
continue
}
client . close ( )
2020-06-27 11:38:35 +00:00
2020-08-31 22:01:17 +00:00
case req := <- p . clientDescribe :
// create path if not exist
if _ , ok := p . paths [ req . pathName ] ; ! ok {
p . paths [ req . pathName ] = newPath ( p , req . pathName , p . findConfForPathName ( req . pathName ) , false )
}
2020-06-27 11:38:35 +00:00
2020-08-31 22:01:17 +00:00
p . paths [ req . pathName ] . onDescribe ( req . client )
2020-06-27 11:38:35 +00:00
2020-08-31 22:01:17 +00:00
case req := <- p . clientAnnounce :
// create path if not exist
if path , ok := p . paths [ req . pathName ] ; ! ok {
p . paths [ req . pathName ] = newPath ( p , req . pathName , p . findConfForPathName ( req . pathName ) , false )
2020-08-30 13:27:03 +00:00
2020-08-31 22:01:17 +00:00
} else {
if path . publisher != nil {
req . res <- fmt . Errorf ( "someone is already publishing on path '%s'" , req . pathName )
continue
2020-07-29 21:30:42 +00:00
}
2020-08-31 22:01:17 +00:00
}
2020-06-30 13:12:39 +00:00
2020-08-31 22:01:17 +00:00
p . paths [ req . pathName ] . publisher = req . client
2020-09-05 12:51:36 +00:00
p . paths [ req . pathName ] . publisherTrackCount = req . trackCount
p . paths [ req . pathName ] . publisherSdp = req . sdp
2020-08-31 13:31:37 +00:00
2020-08-31 22:01:17 +00:00
req . client . path = p . paths [ req . pathName ]
req . client . state = clientStatePreRecord
req . res <- nil
2020-06-27 11:38:35 +00:00
2020-08-31 22:01:17 +00:00
case req := <- p . clientSetupPlay :
path , ok := p . paths [ req . pathName ]
if ! ok || ! path . publisherReady {
req . res <- fmt . Errorf ( "no one is publishing on path '%s'" , req . pathName )
continue
}
2020-06-27 11:38:35 +00:00
2020-09-05 12:51:36 +00:00
if req . trackId >= path . publisherTrackCount {
2020-08-31 22:01:17 +00:00
req . res <- fmt . Errorf ( "track %d does not exist" , req . trackId )
continue
}
req . client . path = path
req . client . state = clientStatePrePlay
req . res <- nil
case client := <- p . clientPlay :
2020-09-03 14:24:39 +00:00
atomic . AddInt64 ( & p . countReader , 1 )
2020-08-31 22:01:17 +00:00
client . state = clientStatePlay
2020-09-18 22:08:05 +00:00
p . readersMap . add ( client )
2020-08-31 22:01:17 +00:00
case client := <- p . clientRecord :
2020-09-03 14:24:39 +00:00
atomic . AddInt64 ( & p . countPublisher , 1 )
2020-08-31 22:01:17 +00:00
client . state = clientStateRecord
2020-09-05 11:19:55 +00:00
if client . streamProtocol == gortsplib . StreamProtocolUDP {
2020-08-31 22:01:17 +00:00
for trackId , track := range client . streamTracks {
2020-09-18 22:08:05 +00:00
addr := makeUDPPublisherAddr ( client . ip ( ) , track . rtpPort )
p . udpPublishersMap . add ( addr , & udpPublisher {
2020-08-31 22:01:17 +00:00
client : client ,
trackId : trackId ,
streamType : gortsplib . StreamTypeRtp ,
2020-09-18 22:08:05 +00:00
} )
2020-06-30 13:12:39 +00:00
2020-09-18 22:08:05 +00:00
addr = makeUDPPublisherAddr ( client . ip ( ) , track . rtcpPort )
p . udpPublishersMap . add ( addr , & udpPublisher {
2020-08-31 22:01:17 +00:00
client : client ,
trackId : trackId ,
streamType : gortsplib . StreamTypeRtcp ,
2020-09-18 22:08:05 +00:00
} )
2020-08-05 08:18:38 +00:00
}
2020-08-31 22:01:17 +00:00
}
2020-08-05 12:59:38 +00:00
2020-08-31 22:01:17 +00:00
client . path . onPublisherSetReady ( )
2020-06-27 11:38:35 +00:00
2020-08-31 22:01:17 +00:00
case source := <- p . sourceReady :
2020-08-31 22:13:27 +00:00
source . path . log ( "source ready" )
2020-08-31 22:01:17 +00:00
source . path . onPublisherSetReady ( )
2020-06-30 13:12:39 +00:00
2020-08-31 22:01:17 +00:00
case source := <- p . sourceNotReady :
2020-08-31 22:13:27 +00:00
source . path . log ( "source not ready" )
2020-08-31 22:01:17 +00:00
source . path . onPublisherSetNotReady ( )
2020-06-30 13:12:39 +00:00
2020-08-31 22:01:17 +00:00
case <- p . terminate :
break outer
2020-06-27 11:38:35 +00:00
}
}
go func ( ) {
2020-08-31 22:01:17 +00:00
for {
select {
case req , ok := <- p . metricsGather :
if ! ok {
return
}
req . res <- nil
case <- p . clientNew :
case <- p . clientClose :
case <- p . clientDescribe :
case req := <- p . clientAnnounce :
req . res <- fmt . Errorf ( "terminated" )
2020-07-30 15:30:50 +00:00
2020-08-31 22:01:17 +00:00
case req := <- p . clientSetupPlay :
req . res <- fmt . Errorf ( "terminated" )
2020-06-27 11:38:35 +00:00
2020-08-31 22:01:17 +00:00
case <- p . clientPlay :
case <- p . clientRecord :
case <- p . sourceReady :
case <- p . sourceNotReady :
2020-06-27 11:38:35 +00:00
}
}
} ( )
2020-08-30 11:31:46 +00:00
for _ , p := range p . paths {
2020-09-03 14:02:58 +00:00
p . onClose ( true )
2020-08-30 11:31:46 +00:00
}
2020-08-29 17:48:41 +00:00
p . serverRtsp . close ( )
2020-08-30 12:27:26 +00:00
if p . serverRtcp != nil {
2020-08-17 16:22:14 +00:00
p . serverRtcp . close ( )
2020-08-30 12:27:26 +00:00
}
if p . serverRtp != nil {
2020-08-17 16:22:14 +00:00
p . serverRtp . close ( )
}
2020-06-27 11:38:35 +00:00
for c := range p . clients {
2020-08-31 20:29:30 +00:00
c . close ( )
2020-07-29 21:30:42 +00:00
<- 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-08-30 12:27:26 +00:00
if p . pprof != nil {
p . pprof . close ( )
}
2020-07-30 17:21:36 +00:00
if p . logFile != nil {
p . logFile . Close ( )
}
2020-08-31 22:01:17 +00:00
close ( p . metricsGather )
close ( p . clientNew )
close ( p . clientClose )
close ( p . clientDescribe )
close ( p . clientAnnounce )
close ( p . clientSetupPlay )
close ( p . clientPlay )
close ( p . clientRecord )
close ( p . sourceReady )
close ( p . sourceNotReady )
2020-06-27 11:38:35 +00:00
close ( p . done )
}
func ( p * program ) close ( ) {
2020-08-31 22:01:17 +00:00
close ( p . terminate )
2020-06-27 11:38:35 +00:00
<- p . done
}
2020-08-30 11:20:18 +00:00
func ( p * program ) findConfForPathName ( name string ) * confPath {
2020-08-05 09:49:36 +00:00
if confp , ok := p . conf . Paths [ name ] ; ok {
2020-07-30 11:31:18 +00:00
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
}
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-08-30 12:10:05 +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
}