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-05-10 13:33:42 +00:00
type program struct {
2020-08-05 12:59:38 +00:00
conf * conf
2020-09-18 22:19:55 +00:00
logHandler * logHandler
2020-08-05 12:59:38 +00:00
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-10-03 19:10:41 +00:00
serverUdpRtp * serverUDP
serverUdpRtcp * serverUDP
serverTcp * 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-22 06:41:04 +00:00
// use pointers to avoid a crash on 32bit platforms
// https://github.com/golang/go/issues/9959
2020-10-03 19:10:41 +00:00
countClients * int64
countPublishers * int64
countReaders * int64
countSourcesRtsp * int64
countSourcesRtspRunning * int64
countSourcesRtmp * int64
countSourcesRtmpRunning * int64
clientNew chan net . Conn
clientClose chan * client
clientDescribe chan clientDescribeReq
clientAnnounce chan clientAnnounceReq
clientSetupPlay chan clientSetupPlayReq
clientPlay chan * client
clientRecord chan * client
sourceRtspReady chan * sourceRtsp
sourceRtspNotReady chan * sourceRtsp
sourceRtmpReady chan * sourceRtmp
sourceRtmpNotReady chan * sourceRtmp
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-09-18 22:19:55 +00:00
logHandler , err := newLogHandler ( conf . logDestinationsParsed , conf . LogFile )
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-09-18 22:19:55 +00:00
logHandler : logHandler ,
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-09-22 06:58:38 +00:00
countClients : func ( ) * int64 {
2020-09-22 06:41:04 +00:00
v := int64 ( 0 )
return & v
} ( ) ,
2020-09-22 06:58:38 +00:00
countPublishers : func ( ) * int64 {
2020-09-22 06:41:04 +00:00
v := int64 ( 0 )
return & v
} ( ) ,
2020-09-22 06:58:38 +00:00
countReaders : func ( ) * int64 {
v := int64 ( 0 )
return & v
} ( ) ,
2020-10-03 19:10:41 +00:00
countSourcesRtsp : func ( ) * int64 {
2020-09-22 06:58:38 +00:00
v := int64 ( 0 )
return & v
} ( ) ,
2020-10-03 19:10:41 +00:00
countSourcesRtspRunning : func ( ) * int64 {
2020-09-22 06:41:04 +00:00
v := int64 ( 0 )
return & v
} ( ) ,
2020-10-03 19:10:41 +00:00
countSourcesRtmp : func ( ) * int64 {
v := int64 ( 0 )
return & v
} ( ) ,
countSourcesRtmpRunning : func ( ) * int64 {
v := int64 ( 0 )
return & v
} ( ) ,
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 ) ,
sourceRtspReady : make ( chan * sourceRtsp ) ,
sourceRtspNotReady : make ( chan * sourceRtsp ) ,
sourceRtmpReady : make ( chan * sourceRtmp ) ,
sourceRtmpNotReady : make ( chan * sourceRtmp ) ,
terminate : make ( chan struct { } ) ,
done : make ( chan struct { } ) ,
2020-06-30 13:12:39 +00:00
}
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-09-19 15:13:45 +00:00
for name , pathConf := range conf . Paths {
if pathConf . regexp == nil {
p . paths [ name ] = newPath ( p , name , pathConf )
2020-08-30 12:15:00 +00:00
}
}
2020-09-05 11:19:55 +00:00
if _ , ok := conf . protocolsParsed [ gortsplib . StreamProtocolUDP ] ; ok {
2020-10-03 19:10:41 +00:00
p . serverUdpRtp , 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-10-03 19:10:41 +00:00
p . serverUdpRtcp , 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-10-03 19:10:41 +00:00
p . serverTcp , 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-22 06:58:38 +00:00
countClients := atomic . LoadInt64 ( p . countClients )
countPublishers := atomic . LoadInt64 ( p . countPublishers )
countReaders := atomic . LoadInt64 ( p . countReaders )
2020-09-03 14:24:39 +00:00
2020-09-19 21:37:54 +00:00
log . Printf ( fmt . Sprintf ( "[%d/%d/%d] " + format , append ( [ ] interface { } { countClients ,
countPublishers , countReaders } , args ... ) ... ) )
2020-09-14 11:41:10 +00:00
}
2020-07-30 17:21:36 +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 ( )
}
2020-10-03 19:10:41 +00:00
if p . serverUdpRtp != nil {
go p . serverUdpRtp . run ( )
2020-08-30 12:27:26 +00:00
}
2020-10-03 19:10:41 +00:00
if p . serverUdpRtcp != nil {
go p . serverUdpRtcp . run ( )
2020-08-30 12:27:26 +00:00
}
2020-10-03 19:10:41 +00:00
go p . serverTcp . run ( )
2020-08-30 12:27:26 +00:00
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 conn := <- p . clientNew :
c := newClient ( p , conn )
p . clients [ c ] = struct { } { }
2020-09-22 06:58:38 +00:00
atomic . AddInt64 ( p . countClients , 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 :
2020-10-03 19:10:41 +00:00
// create path if it doesn't exist
2020-08-31 22:01:17 +00:00
if _ , ok := p . paths [ req . pathName ] ; ! ok {
2020-09-19 15:13:45 +00:00
p . paths [ req . pathName ] = newPath ( p , req . pathName , req . pathConf )
2020-08-31 22:01:17 +00:00
}
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 :
2020-10-03 19:10:41 +00:00
// create path if it doesn't exist
2020-08-31 22:01:17 +00:00
if path , ok := p . paths [ req . pathName ] ; ! ok {
2020-09-19 15:13:45 +00:00
p . paths [ req . pathName ] = newPath ( p , req . pathName , req . pathConf )
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-22 06:58:38 +00:00
atomic . AddInt64 ( p . countReaders , 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-22 06:58:38 +00:00
atomic . AddInt64 ( p . countPublishers , 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-10-03 19:10:41 +00:00
case s := <- p . sourceRtspReady :
s . path . onPublisherSetReady ( )
case s := <- p . sourceRtspNotReady :
s . path . onPublisherSetNotReady ( )
case s := <- p . sourceRtmpReady :
s . path . onPublisherSetReady ( )
2020-06-30 13:12:39 +00:00
2020-10-03 19:10:41 +00:00
case s := <- p . sourceRtmpNotReady :
s . 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 {
2020-09-19 21:37:54 +00:00
case _ , ok := <- p . clientNew :
2020-08-31 22:01:17 +00:00
if ! ok {
return
}
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 :
2020-10-03 19:10:41 +00:00
case <- p . sourceRtspReady :
case <- p . sourceRtspNotReady :
case <- p . sourceRtmpReady :
case <- p . sourceRtmpNotReady :
2020-06-27 11:38:35 +00:00
}
}
} ( )
2020-09-19 11:51:55 +00:00
p . udpPublishersMap . clear ( )
p . readersMap . clear ( )
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-10-03 19:10:41 +00:00
p . serverTcp . close ( )
2020-08-29 17:48:41 +00:00
2020-10-03 19:10:41 +00:00
if p . serverUdpRtcp != nil {
p . serverUdpRtcp . close ( )
2020-08-30 12:27:26 +00:00
}
2020-10-03 19:10:41 +00:00
if p . serverUdpRtp != nil {
p . serverUdpRtp . close ( )
2020-08-17 16:22:14 +00:00
}
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-09-18 22:19:55 +00:00
p . logHandler . close ( )
2020-07-30 17:21:36 +00:00
2020-08-31 22:01:17 +00:00
close ( p . clientNew )
close ( p . clientClose )
close ( p . clientDescribe )
close ( p . clientAnnounce )
close ( p . clientSetupPlay )
close ( p . clientPlay )
close ( p . clientRecord )
2020-10-03 19:10:41 +00:00
close ( p . sourceRtspReady )
close ( p . sourceRtspNotReady )
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
}
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
}