mediamtx/main.go

639 lines
14 KiB
Go
Raw Normal View History

2019-12-28 21:07:03 +00:00
package main
import (
"fmt"
"io"
2019-12-28 21:07:03 +00:00
"log"
"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"
"time"
2019-12-28 21:07:03 +00:00
"github.com/aler9/gortsplib"
"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"
)
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{}
client *client
2020-06-27 11:38:35 +00:00
}
func (programEventClientClose) isProgramEvent() {}
type programEventClientDescribe struct {
client *client
path string
2020-06-27 11:38:35 +00:00
}
func (programEventClientDescribe) isProgramEvent() {}
2020-06-27 11:38:35 +00:00
type programEventClientAnnounce struct {
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 {
res chan error
client *client
2020-06-27 11:38:35 +00:00
path string
2020-07-19 15:54:31 +00:00
protocol gortsplib.StreamProtocol
2020-06-27 11:38:35 +00:00
rtpPort int
rtcpPort int
}
func (programEventClientSetupPlay) isProgramEvent() {}
type programEventClientSetupRecord struct {
res chan error
client *client
2020-07-19 15:54:31 +00:00
protocol gortsplib.StreamProtocol
2020-06-27 11:38:35 +00:00
rtpPort int
rtcpPort int
}
func (programEventClientSetupRecord) isProgramEvent() {}
type programEventClientPlay1 struct {
res chan error
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{}
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{}
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{}
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{}
client *client
2020-07-13 09:12:20 +00:00
}
func (programEventClientRecordStop) isProgramEvent() {}
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
}
func (programEventClientFrameUdp) isProgramEvent() {}
2020-06-27 11:38:35 +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
}
func (programEventClientFrameTcp) isProgramEvent() {}
type programEventSourceReady struct {
2020-07-14 07:19:07 +00:00
source *source
}
func (programEventSourceReady) isProgramEvent() {}
type programEventSourceNotReady struct {
2020-07-14 07:19:07 +00:00
source *source
}
func (programEventSourceNotReady) isProgramEvent() {}
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
}
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 {
conf *conf
logFile *os.File
2020-07-30 15:30:50 +00:00
metrics *metrics
serverRtsp *serverTcp
serverRtp *serverUdp
serverRtcp *serverUdp
2020-07-14 07:19:07 +00:00
sources []*source
clients map[*client]struct{}
paths map[string]*path
2020-07-31 16:12:42 +00:00
cmds []*exec.Cmd
2020-06-27 12:18:16 +00:00
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
}
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()
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
kingpin.MustParse(k.Parse(args))
2020-05-11 07:31:56 +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
}
conf, err := loadConf(*argConfPath, stdin)
if err != nil {
return nil, err
}
p := &program{
conf: conf,
clients: make(map[*client]struct{}),
paths: make(map[string]*path),
events: make(chan programEvent),
done: make(chan struct{}),
}
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
}
2019-12-28 21:07:03 +00:00
}
2020-07-30 15:30:50 +00:00
if conf.Metrics {
p.metrics = newMetrics(p)
}
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 {
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{}) {
line := fmt.Sprintf("[%d/%d/%d] "+format, append([]interface{}{len(p.clients),
p.publisherCount, p.readerCount}, args...)...)
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() {
checkPathsTicker := time.NewTicker(5 * time.Second)
defer checkPathsTicker.Stop()
2020-06-27 11:38:35 +00:00
outer:
for {
select {
case <-checkPathsTicker.C:
for _, path := range p.paths {
path.check()
2020-06-27 11:38:35 +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,
}
case programEventClientNew:
2020-07-30 11:31:18 +00:00
c := newClient(p, evt.nconn)
p.clients[c] = struct{}{}
c.log("connected")
2020-06-27 11:38:35 +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 {
if path.publisher == evt.client {
2020-07-30 11:31:18 +00:00
path.publisherRemove()
2020-07-30 11:31:18 +00:00
if !path.permanent {
delete(p.paths, evt.client.pathId)
}
}
}
2020-06-27 11:38:35 +00:00
}
evt.client.log("disconnected")
close(evt.done)
2020-06-27 11:38:35 +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)}
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
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-30 11:31:18 +00:00
p.paths[evt.path].publisher = evt.client
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
evt.res <- nil
2020-06-27 11:38:35 +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
if len(evt.client.streamTracks) >= len(path.publisherSdpParsed.MediaDescriptions) {
evt.res <- fmt.Errorf("all the tracks have already been setup")
continue
}
2020-07-30 11:31:18 +00:00
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.res <- nil
2020-06-27 11:38:35 +00:00
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.res <- nil
2020-06-27 11:38:35 +00:00
case programEventClientPlay1:
2020-07-30 11:31:18 +00:00
path, ok := p.paths[evt.client.pathId]
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)
continue
}
2020-07-13 09:12:20 +00:00
if len(evt.client.streamTracks) != len(path.publisherSdpParsed.MediaDescriptions) {
evt.res <- fmt.Errorf("not all tracks have been setup")
continue
}
2020-06-27 11:38:35 +00:00
evt.res <- nil
2020-07-13 09:12:20 +00:00
case programEventClientPlay2:
p.readerCount += 1
evt.client.state = clientStatePlay
close(evt.done)
2020-07-13 09:12:20 +00:00
case programEventClientPlayStop:
p.readerCount -= 1
evt.client.state = clientStatePrePlay
close(evt.done)
case programEventClientRecord:
p.publisherCount += 1
evt.client.state = clientStateRecord
2020-07-30 11:31:18 +00:00
p.paths[evt.client.pathId].publisherSetReady()
close(evt.done)
2020-07-13 09:12:20 +00:00
case programEventClientRecordStop:
p.publisherCount -= 1
evt.client.state = clientStatePreRecord
2020-07-30 11:31:18 +00:00
p.paths[evt.client.pathId].publisherSetNotReady()
close(evt.done)
2020-06-27 11:38:35 +00:00
case programEventClientFrameUdp:
client, trackId := p.findClientPublisher(evt.addr, evt.streamType)
if client == nil {
continue
}
2020-06-27 11:38:35 +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
case programEventClientFrameTcp:
p.forwardFrame(evt.path, evt.trackId, evt.streamType, evt.buf)
2020-06-27 11:38:35 +00:00
case programEventSourceReady:
evt.source.log("ready")
2020-07-30 11:31:18 +00:00
p.paths[evt.source.pathId].publisherSetReady()
case programEventSourceNotReady:
evt.source.log("not ready")
2020-07-30 11:31:18 +00:00
p.paths[evt.source.pathId].publisherSetNotReady()
case programEventSourceFrame:
2020-07-30 11:31:18 +00:00
p.forwardFrame(evt.source.pathId, evt.trackId, evt.streamType, evt.buf)
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)
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 {
s.events <- sourceEventTerminate{}
<-s.done
}
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 {
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()
}
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
}
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-30 11:31:18 +00:00
if confp, ok := p.conf.Paths["all"]; ok {
return confp
}
return nil
}
func (p *program) findClientPublisher(addr *net.UDPAddr, streamType gortsplib.StreamType) (*client, int) {
for _, path := range p.paths {
cl, ok := path.publisher.(*client)
2020-07-11 14:40:19 +00:00
if !ok {
continue
}
2020-07-19 15:54:31 +00:00
if cl.streamProtocol != gortsplib.StreamProtocolUdp ||
2020-07-13 07:45:45 +00:00
cl.state != clientStateRecord ||
2020-07-11 14:40:19 +00:00
!cl.ip().Equal(addr.IP) {
continue
}
for i, t := range cl.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 {
return cl, i
}
} else {
if t.rtcpPort == addr.Port {
return cl, i
}
}
}
}
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 {
if c.pathId == path && c.state == clientStatePlay {
if c.streamProtocol == gortsplib.StreamProtocolUdp {
2020-07-12 20:53:22 +00:00
if streamType == gortsplib.StreamTypeRtp {
2020-07-30 15:30:50 +00:00
p.serverRtp.write(&udpAddrBufPair{
2020-07-11 14:40:19 +00:00
addr: &net.UDPAddr{
2020-07-30 11:31:18 +00:00
IP: c.ip(),
Zone: c.zone(),
Port: c.streamTracks[trackId].rtpPort,
2020-07-11 14:40:19 +00:00
},
buf: frame,
})
2020-06-27 11:38:35 +00:00
} else {
2020-07-30 15:30:50 +00:00
p.serverRtcp.write(&udpAddrBufPair{
2020-07-11 14:40:19 +00:00
addr: &net.UDPAddr{
2020-07-30 11:31:18 +00:00
IP: c.ip(),
Zone: c.zone(),
Port: c.streamTracks[trackId].rtcpPort,
2020-07-11 14:40:19 +00:00
},
buf: frame,
})
2020-06-27 11:38:35 +00:00
}
} else {
2020-07-30 11:31:18 +00:00
buf := c.writeBuf.swap()
buf = buf[:len(frame)]
copy(buf, frame)
2020-07-30 11:31:18 +00:00
c.events <- clientEventFrameTcp{
frame: &gortsplib.InterleavedFrame{
2020-07-13 07:45:45 +00:00
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() {
_, err := newProgram(os.Args[1:], os.Stdin)
2019-12-28 21:07:03 +00:00
if err != nil {
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
}