mediamtx/main.go

237 lines
5.3 KiB
Go
Raw Normal View History

2019-12-28 21:07:03 +00:00
package main
import (
"fmt"
"log"
"net"
"os"
2020-01-03 21:39:55 +00:00
"regexp"
"strings"
2019-12-28 21:07:03 +00:00
"sync"
"time"
2019-12-28 21:07:03 +00:00
2020-01-26 11:41:26 +00:00
"github.com/aler9/gortsplib"
2019-12-28 21:07:03 +00:00
"gopkg.in/alecthomas/kingpin.v2"
)
var Version string = "v0.0.0"
const (
_READ_TIMEOUT = 5 * time.Second
_WRITE_TIMEOUT = 5 * time.Second
)
2019-12-31 12:48:17 +00:00
type trackFlow int
const (
_TRACK_FLOW_RTP trackFlow = iota
_TRACK_FLOW_RTCP
)
type track struct {
rtpPort int
rtcpPort int
}
type streamProtocol int
const (
2020-01-20 16:20:53 +00:00
_STREAM_PROTOCOL_UDP streamProtocol = iota
2019-12-31 12:48:17 +00:00
_STREAM_PROTOCOL_TCP
)
func (s streamProtocol) String() string {
if s == _STREAM_PROTOCOL_UDP {
return "udp"
}
return "tcp"
}
2019-12-28 21:07:03 +00:00
type program struct {
protocols map[streamProtocol]struct{}
rtspPort int
rtpPort int
rtcpPort int
publishUser string
publishPass string
preScript string
postScript string
mutex sync.RWMutex
rtspl *serverTcpListener
rtpl *serverUdpListener
rtcpl *serverUdpListener
2020-05-03 10:38:11 +00:00
clients map[*serverClient]struct{}
publishers map[string]*serverClient
2019-12-28 21:07:03 +00:00
}
func newProgram() (*program, error) {
kingpin.CommandLine.Help = "rtsp-simple-server " + Version + "\n\n" +
"RTSP server."
argVersion := kingpin.Flag("version", "print rtsp-simple-server version").Bool()
argProtocolsStr := kingpin.Flag("protocols", "supported protocols").Default("udp,tcp").String()
argRtspPort := kingpin.Flag("rtsp-port", "port of the RTSP TCP listener").Default("8554").Int()
argRtpPort := kingpin.Flag("rtp-port", "port of the RTP UDP listener").Default("8000").Int()
argRtcpPort := kingpin.Flag("rtcp-port", "port of the RTCP UDP listener").Default("8001").Int()
argPublishUser := kingpin.Flag("publish-user", "optional username required to publish").Default("").String()
argPublishPass := kingpin.Flag("publish-pass", "optional password required to publish").Default("").String()
argPreScript := kingpin.Flag("pre-script", "optional script to run on client connect").Default("").String()
argPostScript := kingpin.Flag("post-script", "optional script to run on client disconnect").Default("").String()
kingpin.Parse()
version := *argVersion
protocolsStr := *argProtocolsStr
rtspPort := *argRtspPort
rtpPort := *argRtpPort
rtcpPort := *argRtcpPort
publishUser := *argPublishUser
publishPass := *argPublishPass
preScript := *argPreScript
postScript := *argPostScript
if version == true {
fmt.Println("rtsp-simple-server " + Version)
os.Exit(0)
}
2020-01-26 11:58:56 +00:00
if rtspPort == 0 {
return nil, fmt.Errorf("rtsp port not provided")
}
if rtpPort == 0 {
return nil, fmt.Errorf("rtp port not provided")
}
if rtcpPort == 0 {
return nil, fmt.Errorf("rtcp port not provided")
}
if (rtpPort % 2) != 0 {
return nil, fmt.Errorf("rtp port must be even")
}
if rtcpPort != (rtpPort + 1) {
return nil, fmt.Errorf("rtcp port must be rtp port plus 1")
}
protocols := make(map[streamProtocol]struct{})
for _, proto := range strings.Split(protocolsStr, ",") {
switch proto {
case "udp":
protocols[_STREAM_PROTOCOL_UDP] = struct{}{}
case "tcp":
protocols[_STREAM_PROTOCOL_TCP] = struct{}{}
default:
return nil, fmt.Errorf("unsupported protocol: %s", proto)
}
}
if len(protocols) == 0 {
2020-01-26 11:58:56 +00:00
return nil, fmt.Errorf("no protocols provided")
}
if publishUser != "" {
if !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(publishUser) {
return nil, fmt.Errorf("publish username must be alphanumeric")
}
}
if publishPass != "" {
if !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(publishPass) {
return nil, fmt.Errorf("publish password must be alphanumeric")
2020-01-03 21:39:55 +00:00
}
}
if publishUser != "" && publishPass == "" || publishUser == "" && publishPass != "" {
return nil, fmt.Errorf("publish username and password must be both filled")
}
2020-01-03 21:39:55 +00:00
log.Printf("rtsp-simple-server %s", Version)
2019-12-28 21:07:03 +00:00
p := &program{
protocols: protocols,
rtspPort: rtspPort,
rtpPort: rtpPort,
rtcpPort: rtcpPort,
publishUser: publishUser,
publishPass: publishPass,
preScript: preScript,
postScript: postScript,
2020-05-03 10:38:11 +00:00
clients: make(map[*serverClient]struct{}),
publishers: make(map[string]*serverClient),
2019-12-28 21:07:03 +00:00
}
var err error
2020-01-20 14:22:01 +00:00
p.rtpl, err = newServerUdpListener(p, rtpPort, _TRACK_FLOW_RTP)
2019-12-28 21:07:03 +00:00
if err != nil {
return nil, err
}
2020-01-20 14:22:01 +00:00
p.rtcpl, err = newServerUdpListener(p, rtcpPort, _TRACK_FLOW_RTCP)
2019-12-28 21:07:03 +00:00
if err != nil {
return nil, err
}
2020-01-20 14:22:01 +00:00
p.rtspl, err = newServerTcpListener(p)
2019-12-28 21:07:03 +00:00
if err != nil {
return nil, err
}
return p, nil
}
func (p *program) run() {
2019-12-29 00:52:52 +00:00
go p.rtpl.run()
go p.rtcpl.run()
go p.rtspl.run()
2019-12-28 21:07:03 +00:00
2019-12-29 00:52:52 +00:00
infty := make(chan struct{})
<-infty
2019-12-28 21:07:03 +00:00
}
2019-12-31 13:55:46 +00:00
func (p *program) forwardTrack(path string, id int, flow trackFlow, frame []byte) {
2019-12-29 00:05:25 +00:00
for c := range p.clients {
if c.path == path && c.state == _CLIENT_STATE_PLAY {
2019-12-31 12:48:17 +00:00
if c.streamProtocol == _STREAM_PROTOCOL_UDP {
if flow == _TRACK_FLOW_RTP {
2020-01-26 17:08:15 +00:00
p.rtpl.chanWrite <- &udpWrite{
addr: &net.UDPAddr{
IP: c.ip(),
Zone: c.zone(),
2020-01-26 17:08:15 +00:00
Port: c.streamTracks[id].rtpPort,
},
buf: frame,
}
2019-12-31 12:48:17 +00:00
} else {
2020-01-26 17:08:15 +00:00
p.rtcpl.chanWrite <- &udpWrite{
addr: &net.UDPAddr{
IP: c.ip(),
Zone: c.zone(),
2020-01-26 17:08:15 +00:00
Port: c.streamTracks[id].rtcpPort,
},
buf: frame,
}
2019-12-31 12:48:17 +00:00
}
2019-12-29 00:05:25 +00:00
} else {
2020-01-26 17:08:15 +00:00
c.chanWrite <- &gortsplib.InterleavedFrame{
2020-01-26 11:41:26 +00:00
Channel: trackToInterleavedChannel(id, flow),
Content: frame,
2020-01-26 17:08:15 +00:00
}
2019-12-29 00:05:25 +00:00
}
}
}
}
2019-12-28 21:07:03 +00:00
func main() {
p, err := newProgram()
2019-12-28 21:07:03 +00:00
if err != nil {
2019-12-29 11:32:54 +00:00
log.Fatal("ERR: ", err)
2019-12-28 21:07:03 +00:00
}
p.run()
}