Browse Source

move conf into dedicated folder

pull/169/head
aler9 6 years ago
parent
commit
606806777a
  1. 27
      client.go
  2. 140
      conf/conf.go
  3. 6
      confenv/confenv.go
  4. 11
      main.go
  5. 10
      main_test.go
  6. 7
      path.go
  7. 16
      sourcertsp.go
  8. 47
      utils.go

27
client.go

@ -16,6 +16,7 @@ import (
"github.com/aler9/gortsplib/headers" "github.com/aler9/gortsplib/headers"
"github.com/aler9/gortsplib/rtcpreceiver" "github.com/aler9/gortsplib/rtcpreceiver"
"github.com/aler9/rtsp-simple-server/conf"
"github.com/aler9/rtsp-simple-server/externalcmd" "github.com/aler9/rtsp-simple-server/externalcmd"
) )
@ -27,14 +28,14 @@ const (
type clientDescribeReq struct { type clientDescribeReq struct {
client *client client *client
pathName string pathName string
pathConf *pathConf pathConf *conf.PathConf
} }
type clientAnnounceReq struct { type clientAnnounceReq struct {
res chan error res chan error
client *client client *client
pathName string pathName string
pathConf *pathConf pathConf *conf.PathConf
trackCount int trackCount int
sdp []byte sdp []byte
} }
@ -260,7 +261,7 @@ func (c *client) authenticate(ips []interface{}, user string, pass string, req *
if c.authHelper == nil || c.authUser != user || c.authPass != pass { if c.authHelper == nil || c.authUser != user || c.authPass != pass {
c.authUser = user c.authUser = user
c.authPass = pass c.authPass = pass
c.authHelper = auth.NewServer(user, pass, c.p.conf.authMethodsParsed) c.authHelper = auth.NewServer(user, pass, c.p.conf.AuthMethodsParsed)
} }
err := c.authHelper.ValidateHeader(req.Header["Authorization"], req.Method, req.Url) err := c.authHelper.ValidateHeader(req.Header["Authorization"], req.Method, req.Url)
@ -371,13 +372,13 @@ func (c *client) handleRequest(req *base.Request) error {
pathName = removeQueryFromPath(pathName) pathName = removeQueryFromPath(pathName)
pathConf, err := c.p.conf.checkPathNameAndFindConf(pathName) pathConf, err := c.p.conf.CheckPathNameAndFindConf(pathName)
if err != nil { if err != nil {
c.writeResError(cseq, base.StatusBadRequest, err) c.writeResError(cseq, base.StatusBadRequest, err)
return errRunTerminate return errRunTerminate
} }
err = c.authenticate(pathConf.readIpsParsed, pathConf.ReadUser, pathConf.ReadPass, req) err = c.authenticate(pathConf.ReadIpsParsed, pathConf.ReadUser, pathConf.ReadPass, req)
if err != nil { if err != nil {
if err == errAuthCritical { if err == errAuthCritical {
return errRunTerminate return errRunTerminate
@ -401,13 +402,13 @@ func (c *client) handleRequest(req *base.Request) error {
pathName = removeQueryFromPath(pathName) pathName = removeQueryFromPath(pathName)
pathConf, err := c.p.conf.checkPathNameAndFindConf(pathName) pathConf, err := c.p.conf.CheckPathNameAndFindConf(pathName)
if err != nil { if err != nil {
c.writeResError(cseq, base.StatusBadRequest, err) c.writeResError(cseq, base.StatusBadRequest, err)
return errRunTerminate return errRunTerminate
} }
err = c.authenticate(pathConf.publishIpsParsed, pathConf.PublishUser, pathConf.PublishPass, req) err = c.authenticate(pathConf.PublishIpsParsed, pathConf.PublishUser, pathConf.PublishPass, req)
if err != nil { if err != nil {
if err == errAuthCritical { if err == errAuthCritical {
return errRunTerminate return errRunTerminate
@ -483,13 +484,13 @@ func (c *client) handleRequest(req *base.Request) error {
return errRunTerminate return errRunTerminate
} }
pathConf, err := c.p.conf.checkPathNameAndFindConf(basePath) pathConf, err := c.p.conf.CheckPathNameAndFindConf(basePath)
if err != nil { if err != nil {
c.writeResError(cseq, base.StatusBadRequest, err) c.writeResError(cseq, base.StatusBadRequest, err)
return errRunTerminate return errRunTerminate
} }
err = c.authenticate(pathConf.readIpsParsed, pathConf.ReadUser, pathConf.ReadPass, req) err = c.authenticate(pathConf.ReadIpsParsed, pathConf.ReadUser, pathConf.ReadPass, req)
if err != nil { if err != nil {
if err == errAuthCritical { if err == errAuthCritical {
return errRunTerminate return errRunTerminate
@ -521,7 +522,7 @@ func (c *client) handleRequest(req *base.Request) error {
// play with UDP // play with UDP
if th.Protocol == gortsplib.StreamProtocolUDP { if th.Protocol == gortsplib.StreamProtocolUDP {
if _, ok := c.p.conf.protocolsParsed[gortsplib.StreamProtocolUDP]; !ok { if _, ok := c.p.conf.ProtocolsParsed[gortsplib.StreamProtocolUDP]; !ok {
c.writeResError(cseq, base.StatusUnsupportedTransport, fmt.Errorf("UDP streaming is disabled")) c.writeResError(cseq, base.StatusUnsupportedTransport, fmt.Errorf("UDP streaming is disabled"))
return errRunTerminate return errRunTerminate
} }
@ -572,7 +573,7 @@ func (c *client) handleRequest(req *base.Request) error {
// play with TCP // play with TCP
} else { } else {
if _, ok := c.p.conf.protocolsParsed[gortsplib.StreamProtocolTCP]; !ok { if _, ok := c.p.conf.ProtocolsParsed[gortsplib.StreamProtocolTCP]; !ok {
c.writeResError(cseq, base.StatusUnsupportedTransport, fmt.Errorf("TCP streaming is disabled")) c.writeResError(cseq, base.StatusUnsupportedTransport, fmt.Errorf("TCP streaming is disabled"))
return errRunTerminate return errRunTerminate
} }
@ -629,7 +630,7 @@ func (c *client) handleRequest(req *base.Request) error {
// record with UDP // record with UDP
if th.Protocol == gortsplib.StreamProtocolUDP { if th.Protocol == gortsplib.StreamProtocolUDP {
if _, ok := c.p.conf.protocolsParsed[gortsplib.StreamProtocolUDP]; !ok { if _, ok := c.p.conf.ProtocolsParsed[gortsplib.StreamProtocolUDP]; !ok {
c.writeResError(cseq, base.StatusUnsupportedTransport, fmt.Errorf("UDP streaming is disabled")) c.writeResError(cseq, base.StatusUnsupportedTransport, fmt.Errorf("UDP streaming is disabled"))
return errRunTerminate return errRunTerminate
} }
@ -677,7 +678,7 @@ func (c *client) handleRequest(req *base.Request) error {
// record with TCP // record with TCP
} else { } else {
if _, ok := c.p.conf.protocolsParsed[gortsplib.StreamProtocolTCP]; !ok { if _, ok := c.p.conf.ProtocolsParsed[gortsplib.StreamProtocolTCP]; !ok {
c.writeResError(cseq, base.StatusUnsupportedTransport, fmt.Errorf("TCP streaming is disabled")) c.writeResError(cseq, base.StatusUnsupportedTransport, fmt.Errorf("TCP streaming is disabled"))
return errRunTerminate return errRunTerminate
} }

140
conf.go → conf/conf.go

@ -1,8 +1,8 @@
package main package conf
import ( import (
"fmt" "fmt"
"io" "net"
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
@ -17,12 +17,58 @@ import (
"github.com/aler9/rtsp-simple-server/loghandler" "github.com/aler9/rtsp-simple-server/loghandler"
) )
type pathConf struct { func parseIpCidrList(in []string) ([]interface{}, error) {
regexp *regexp.Regexp if len(in) == 0 {
return nil, nil
}
var ret []interface{}
for _, t := range in {
_, ipnet, err := net.ParseCIDR(t)
if err == nil {
ret = append(ret, ipnet)
continue
}
ip := net.ParseIP(t)
if ip != nil {
ret = append(ret, ip)
continue
}
return nil, fmt.Errorf("unable to parse ip/network '%s'", t)
}
return ret, nil
}
var rePathName = regexp.MustCompile("^[0-9a-zA-Z_\\-/]+$")
func checkPathName(name string) error {
if name == "" {
return fmt.Errorf("cannot be empty")
}
if name[0] == '/' {
return fmt.Errorf("can't begin with a slash")
}
if name[len(name)-1] == '/' {
return fmt.Errorf("can't end with a slash")
}
if !rePathName.MatchString(name) {
return fmt.Errorf("can contain only alfanumeric characters, underscore, minus or slash")
}
return nil
}
type PathConf struct {
Regexp *regexp.Regexp
Source string `yaml:"source"` Source string `yaml:"source"`
sourceUrl *url.URL `` SourceUrl *url.URL `yaml:"-"`
SourceProtocol string `yaml:"sourceProtocol"` SourceProtocol string `yaml:"sourceProtocol"`
sourceProtocolParsed gortsplib.StreamProtocol `` SourceProtocolParsed gortsplib.StreamProtocol `yaml:"-"`
SourceOnDemand bool `yaml:"sourceOnDemand"` SourceOnDemand bool `yaml:"sourceOnDemand"`
RunOnInit string `yaml:"runOnInit"` RunOnInit string `yaml:"runOnInit"`
RunOnDemand string `yaml:"runOnDemand"` RunOnDemand string `yaml:"runOnDemand"`
@ -31,16 +77,16 @@ type pathConf struct {
PublishUser string `yaml:"publishUser"` PublishUser string `yaml:"publishUser"`
PublishPass string `yaml:"publishPass"` PublishPass string `yaml:"publishPass"`
PublishIps []string `yaml:"publishIps"` PublishIps []string `yaml:"publishIps"`
publishIpsParsed []interface{} `` PublishIpsParsed []interface{} `yaml:"-"`
ReadUser string `yaml:"readUser"` ReadUser string `yaml:"readUser"`
ReadPass string `yaml:"readPass"` ReadPass string `yaml:"readPass"`
ReadIps []string `yaml:"readIps"` ReadIps []string `yaml:"readIps"`
readIpsParsed []interface{} `` ReadIpsParsed []interface{} `yaml:"-"`
} }
type conf struct { type Conf struct {
Protocols []string `yaml:"protocols"` Protocols []string `yaml:"protocols"`
protocolsParsed map[gortsplib.StreamProtocol]struct{} `` ProtocolsParsed map[gortsplib.StreamProtocol]struct{} `yaml:"-"`
RtspPort int `yaml:"rtspPort"` RtspPort int `yaml:"rtspPort"`
RtpPort int `yaml:"rtpPort"` RtpPort int `yaml:"rtpPort"`
RtcpPort int `yaml:"rtcpPort"` RtcpPort int `yaml:"rtcpPort"`
@ -48,19 +94,19 @@ type conf struct {
ReadTimeout time.Duration `yaml:"readTimeout"` ReadTimeout time.Duration `yaml:"readTimeout"`
WriteTimeout time.Duration `yaml:"writeTimeout"` WriteTimeout time.Duration `yaml:"writeTimeout"`
AuthMethods []string `yaml:"authMethods"` AuthMethods []string `yaml:"authMethods"`
authMethodsParsed []headers.AuthMethod `` AuthMethodsParsed []headers.AuthMethod `yaml:"-"`
Metrics bool `yaml:"metrics"` Metrics bool `yaml:"metrics"`
Pprof bool `yaml:"pprof"` Pprof bool `yaml:"pprof"`
LogDestinations []string `yaml:"logDestinations"` LogDestinations []string `yaml:"logDestinations"`
logDestinationsParsed map[loghandler.Destination]struct{} `` LogDestinationsParsed map[loghandler.Destination]struct{} `yaml:"-"`
LogFile string `yaml:"logFile"` LogFile string `yaml:"logFile"`
Paths map[string]*pathConf `yaml:"paths"` Paths map[string]*PathConf `yaml:"paths"`
} }
func loadConf(fpath string) (*conf, error) { func Load(fpath string) (*Conf, error) {
conf := &conf{} conf := &Conf{}
// read from file or stdin // read from file
err := func() error { err := func() error {
// rtsp-simple-server.yml is optional // rtsp-simple-server.yml is optional
if fpath == "rtsp-simple-server.yml" { if fpath == "rtsp-simple-server.yml" {
@ -95,20 +141,20 @@ func loadConf(fpath string) (*conf, error) {
if len(conf.Protocols) == 0 { if len(conf.Protocols) == 0 {
conf.Protocols = []string{"udp", "tcp"} conf.Protocols = []string{"udp", "tcp"}
} }
conf.protocolsParsed = make(map[gortsplib.StreamProtocol]struct{}) conf.ProtocolsParsed = make(map[gortsplib.StreamProtocol]struct{})
for _, proto := range conf.Protocols { for _, proto := range conf.Protocols {
switch proto { switch proto {
case "udp": case "udp":
conf.protocolsParsed[gortsplib.StreamProtocolUDP] = struct{}{} conf.ProtocolsParsed[gortsplib.StreamProtocolUDP] = struct{}{}
case "tcp": case "tcp":
conf.protocolsParsed[gortsplib.StreamProtocolTCP] = struct{}{} conf.ProtocolsParsed[gortsplib.StreamProtocolTCP] = struct{}{}
default: default:
return nil, fmt.Errorf("unsupported protocol: %s", proto) return nil, fmt.Errorf("unsupported protocol: %s", proto)
} }
} }
if len(conf.protocolsParsed) == 0 { if len(conf.ProtocolsParsed) == 0 {
return nil, fmt.Errorf("no protocols provided") return nil, fmt.Errorf("no protocols provided")
} }
@ -141,10 +187,10 @@ func loadConf(fpath string) (*conf, error) {
for _, method := range conf.AuthMethods { for _, method := range conf.AuthMethods {
switch method { switch method {
case "basic": case "basic":
conf.authMethodsParsed = append(conf.authMethodsParsed, headers.AuthBasic) conf.AuthMethodsParsed = append(conf.AuthMethodsParsed, headers.AuthBasic)
case "digest": case "digest":
conf.authMethodsParsed = append(conf.authMethodsParsed, headers.AuthDigest) conf.AuthMethodsParsed = append(conf.AuthMethodsParsed, headers.AuthDigest)
default: default:
return nil, fmt.Errorf("unsupported authentication method: %s", method) return nil, fmt.Errorf("unsupported authentication method: %s", method)
@ -154,17 +200,17 @@ func loadConf(fpath string) (*conf, error) {
if len(conf.LogDestinations) == 0 { if len(conf.LogDestinations) == 0 {
conf.LogDestinations = []string{"stdout"} conf.LogDestinations = []string{"stdout"}
} }
conf.logDestinationsParsed = make(map[loghandler.Destination]struct{}) conf.LogDestinationsParsed = make(map[loghandler.Destination]struct{})
for _, dest := range conf.LogDestinations { for _, dest := range conf.LogDestinations {
switch dest { switch dest {
case "stdout": case "stdout":
conf.logDestinationsParsed[loghandler.DestinationStdout] = struct{}{} conf.LogDestinationsParsed[loghandler.DestinationStdout] = struct{}{}
case "file": case "file":
conf.logDestinationsParsed[loghandler.DestinationFile] = struct{}{} conf.LogDestinationsParsed[loghandler.DestinationFile] = struct{}{}
case "syslog": case "syslog":
conf.logDestinationsParsed[loghandler.DestinationSyslog] = struct{}{} conf.LogDestinationsParsed[loghandler.DestinationSyslog] = struct{}{}
default: default:
return nil, fmt.Errorf("unsupported log destination: %s", dest) return nil, fmt.Errorf("unsupported log destination: %s", dest)
@ -175,7 +221,7 @@ func loadConf(fpath string) (*conf, error) {
} }
if len(conf.Paths) == 0 { if len(conf.Paths) == 0 {
conf.Paths = map[string]*pathConf{ conf.Paths = map[string]*PathConf{
"all": {}, "all": {},
} }
} }
@ -188,7 +234,7 @@ func loadConf(fpath string) (*conf, error) {
for name, pconf := range conf.Paths { for name, pconf := range conf.Paths {
if pconf == nil { if pconf == nil {
conf.Paths[name] = &pathConf{} conf.Paths[name] = &PathConf{}
pconf = conf.Paths[name] pconf = conf.Paths[name]
} }
@ -209,7 +255,7 @@ func loadConf(fpath string) (*conf, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid regular expression: %s", name[1:]) return nil, fmt.Errorf("invalid regular expression: %s", name[1:])
} }
pconf.regexp = pathRegexp pconf.Regexp = pathRegexp
} }
if pconf.Source == "" { if pconf.Source == "" {
@ -217,20 +263,20 @@ func loadConf(fpath string) (*conf, error) {
} }
if strings.HasPrefix(pconf.Source, "rtsp://") { if strings.HasPrefix(pconf.Source, "rtsp://") {
if pconf.regexp != nil { if pconf.Regexp != nil {
return nil, fmt.Errorf("a path with a regular expression (or path 'all') cannot have a RTSP source; use another path") return nil, fmt.Errorf("a path with a regular expression (or path 'all') cannot have a RTSP source; use another path")
} }
pconf.sourceUrl, err = url.Parse(pconf.Source) pconf.SourceUrl, err = url.Parse(pconf.Source)
if err != nil { if err != nil {
return nil, fmt.Errorf("'%s' is not a valid url", pconf.Source) return nil, fmt.Errorf("'%s' is not a valid url", pconf.Source)
} }
if pconf.sourceUrl.Port() == "" { if pconf.SourceUrl.Port() == "" {
pconf.sourceUrl.Host += ":554" pconf.SourceUrl.Host += ":554"
} }
if pconf.sourceUrl.User != nil { if pconf.SourceUrl.User != nil {
pass, _ := pconf.sourceUrl.User.Password() pass, _ := pconf.SourceUrl.User.Password()
user := pconf.sourceUrl.User.Username() user := pconf.SourceUrl.User.Username()
if user != "" && pass == "" || if user != "" && pass == "" ||
user == "" && pass != "" { user == "" && pass != "" {
fmt.Errorf("username and password must be both provided") fmt.Errorf("username and password must be both provided")
@ -242,26 +288,26 @@ func loadConf(fpath string) (*conf, error) {
} }
switch pconf.SourceProtocol { switch pconf.SourceProtocol {
case "udp": case "udp":
pconf.sourceProtocolParsed = gortsplib.StreamProtocolUDP pconf.SourceProtocolParsed = gortsplib.StreamProtocolUDP
case "tcp": case "tcp":
pconf.sourceProtocolParsed = gortsplib.StreamProtocolTCP pconf.SourceProtocolParsed = gortsplib.StreamProtocolTCP
default: default:
return nil, fmt.Errorf("unsupported protocol '%s'", pconf.SourceProtocol) return nil, fmt.Errorf("unsupported protocol '%s'", pconf.SourceProtocol)
} }
} else if strings.HasPrefix(pconf.Source, "rtmp://") { } else if strings.HasPrefix(pconf.Source, "rtmp://") {
if pconf.regexp != nil { if pconf.Regexp != nil {
return nil, fmt.Errorf("a path with a regular expression (or path 'all') cannot have a RTMP source; use another path") return nil, fmt.Errorf("a path with a regular expression (or path 'all') cannot have a RTMP source; use another path")
} }
pconf.sourceUrl, err = url.Parse(pconf.Source) pconf.SourceUrl, err = url.Parse(pconf.Source)
if err != nil { if err != nil {
return nil, fmt.Errorf("'%s' is not a valid url", pconf.Source) return nil, fmt.Errorf("'%s' is not a valid url", pconf.Source)
} }
if pconf.sourceUrl.Port() == "" { if pconf.SourceUrl.Port() == "" {
pconf.sourceUrl.Host += ":1935" pconf.SourceUrl.Host += ":1935"
} }
} else if pconf.Source == "record" { } else if pconf.Source == "record" {
@ -280,7 +326,7 @@ func loadConf(fpath string) (*conf, error) {
return nil, fmt.Errorf("publish password must be alphanumeric") return nil, fmt.Errorf("publish password must be alphanumeric")
} }
} }
pconf.publishIpsParsed, err = parseIpCidrList(pconf.PublishIps) pconf.PublishIpsParsed, err = parseIpCidrList(pconf.PublishIps)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -301,12 +347,12 @@ func loadConf(fpath string) (*conf, error) {
if pconf.ReadUser != "" && pconf.ReadPass == "" || pconf.ReadUser == "" && pconf.ReadPass != "" { if pconf.ReadUser != "" && pconf.ReadPass == "" || pconf.ReadUser == "" && pconf.ReadPass != "" {
return nil, fmt.Errorf("read username and password must be both filled") return nil, fmt.Errorf("read username and password must be both filled")
} }
pconf.readIpsParsed, err = parseIpCidrList(pconf.ReadIps) pconf.ReadIpsParsed, err = parseIpCidrList(pconf.ReadIps)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if pconf.regexp != nil && pconf.RunOnInit != "" { if pconf.Regexp != nil && pconf.RunOnInit != "" {
return nil, fmt.Errorf("a path with a regular expression does not support option 'runOnInit'; use another path") return nil, fmt.Errorf("a path with a regular expression does not support option 'runOnInit'; use another path")
} }
} }
@ -314,7 +360,7 @@ func loadConf(fpath string) (*conf, error) {
return conf, nil return conf, nil
} }
func (conf *conf) checkPathNameAndFindConf(name string) (*pathConf, error) { func (conf *Conf) CheckPathNameAndFindConf(name string) (*PathConf, error) {
err := checkPathName(name) err := checkPathName(name)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid path name: %s (%s)", err, name) return nil, fmt.Errorf("invalid path name: %s (%s)", err, name)
@ -327,7 +373,7 @@ func (conf *conf) checkPathNameAndFindConf(name string) (*pathConf, error) {
// regular expression path // regular expression path
for _, pconf := range conf.Paths { for _, pconf := range conf.Paths {
if pconf.regexp != nil && pconf.regexp.MatchString(name) { if pconf.Regexp != nil && pconf.Regexp.MatchString(name) {
return pconf, nil return pconf, nil
} }
} }

6
confenv/confenv.go vendored

@ -94,14 +94,14 @@ func process(env map[string]string, envKey string, rv reflect.Value) error {
case reflect.Struct: case reflect.Struct:
flen := rt.NumField() flen := rt.NumField()
for i := 0; i < flen; i++ { for i := 0; i < flen; i++ {
fieldName := rt.Field(i).Name f := rt.Field(i)
// process only public fields // process only public fields
if fieldName[0] < 'A' || fieldName[0] > 'Z' { if f.Tag.Get("yaml") == "-" {
continue continue
} }
fieldEnvKey := envKey + "_" + strings.ToUpper(fieldName) fieldEnvKey := envKey + "_" + strings.ToUpper(f.Name)
err := process(env, fieldEnvKey, rv.Field(i)) err := process(env, fieldEnvKey, rv.Field(i))
if err != nil { if err != nil {
return err return err

11
main.go

@ -12,6 +12,7 @@ import (
"github.com/aler9/gortsplib" "github.com/aler9/gortsplib"
"gopkg.in/alecthomas/kingpin.v2" "gopkg.in/alecthomas/kingpin.v2"
"github.com/aler9/rtsp-simple-server/conf"
"github.com/aler9/rtsp-simple-server/loghandler" "github.com/aler9/rtsp-simple-server/loghandler"
) )
@ -22,7 +23,7 @@ const (
) )
type program struct { type program struct {
conf *conf conf *conf.Conf
logHandler *loghandler.LogHandler logHandler *loghandler.LogHandler
metrics *metrics metrics *metrics
pprof *pprof pprof *pprof
@ -72,12 +73,12 @@ func newProgram(args []string, stdin io.Reader) (*program, error) {
os.Exit(0) os.Exit(0)
} }
conf, err := loadConf(*argConfPath) conf, err := conf.Load(*argConfPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
logHandler, err := loghandler.New(conf.logDestinationsParsed, conf.LogFile) logHandler, err := loghandler.New(conf.LogDestinationsParsed, conf.LogFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -149,12 +150,12 @@ func newProgram(args []string, stdin io.Reader) (*program, error) {
} }
for name, pathConf := range conf.Paths { for name, pathConf := range conf.Paths {
if pathConf.regexp == nil { if pathConf.Regexp == nil {
p.paths[name] = newPath(p, name, pathConf) p.paths[name] = newPath(p, name, pathConf)
} }
} }
if _, ok := conf.protocolsParsed[gortsplib.StreamProtocolUDP]; ok { if _, ok := conf.ProtocolsParsed[gortsplib.StreamProtocolUDP]; ok {
p.serverUdpRtp, err = newServerUDP(p, conf.RtpPort, gortsplib.StreamTypeRtp) p.serverUdpRtp, err = newServerUDP(p, conf.RtpPort, gortsplib.StreamTypeRtp)
if err != nil { if err != nil {
return nil, err return nil, err

10
main_test.go

@ -12,6 +12,8 @@ import (
"github.com/aler9/gortsplib" "github.com/aler9/gortsplib"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/aler9/rtsp-simple-server/conf"
) )
var ownDockerIp = func() string { var ownDockerIp = func() string {
@ -163,20 +165,20 @@ func TestEnvironment(t *testing.T) {
pa, ok := p.conf.Paths["test2"] pa, ok := p.conf.Paths["test2"]
require.Equal(t, true, ok) require.Equal(t, true, ok)
require.Equal(t, &pathConf{ require.Equal(t, &conf.PathConf{
Source: "record", Source: "record",
}, pa) }, pa)
pa, ok = p.conf.Paths["test"] pa, ok = p.conf.Paths["test"]
require.Equal(t, true, ok) require.Equal(t, true, ok)
require.Equal(t, &pathConf{ require.Equal(t, &conf.PathConf{
Source: "rtsp://testing", Source: "rtsp://testing",
sourceUrl: func() *url.URL { SourceUrl: func() *url.URL {
u, _ := url.Parse("rtsp://testing:554") u, _ := url.Parse("rtsp://testing:554")
return u return u
}(), }(),
SourceProtocol: "tcp", SourceProtocol: "tcp",
sourceProtocolParsed: gortsplib.StreamProtocolTCP, SourceProtocolParsed: gortsplib.StreamProtocolTCP,
}, pa) }, pa)
} }

7
path.go

@ -6,6 +6,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/aler9/rtsp-simple-server/conf"
"github.com/aler9/rtsp-simple-server/externalcmd" "github.com/aler9/rtsp-simple-server/externalcmd"
) )
@ -23,7 +24,7 @@ type source interface {
type path struct { type path struct {
p *program p *program
name string name string
conf *pathConf conf *conf.PathConf
source source source source
sourceReady bool sourceReady bool
sourceTrackCount int sourceTrackCount int
@ -34,7 +35,7 @@ type path struct {
onDemandCmd *externalcmd.ExternalCmd onDemandCmd *externalcmd.ExternalCmd
} }
func newPath(p *program, name string, conf *pathConf) *path { func newPath(p *program, name string, conf *conf.PathConf) *path {
pa := &path{ pa := &path{
p: p, p: p,
name: name, name: name,
@ -189,7 +190,7 @@ func (pa *path) onCheck() {
} }
// remove regular expression paths // remove regular expression paths
if pa.conf.regexp != nil && if pa.conf.Regexp != nil &&
pa.source == nil && pa.source == nil &&
!pa.hasClients() { !pa.hasClients() {
pa.onClose(false) pa.onClose(false)

16
sourcertsp.go

@ -128,7 +128,7 @@ func (s *sourceRtsp) runInnerInner() bool {
go func() { go func() {
defer close(dialDone) defer close(dialDone)
conn, err = gortsplib.NewConnClient(gortsplib.ConnClientConf{ conn, err = gortsplib.NewConnClient(gortsplib.ConnClientConf{
Host: s.path.conf.sourceUrl.Host, Host: s.path.conf.SourceUrl.Host,
ReadTimeout: s.p.conf.ReadTimeout, ReadTimeout: s.p.conf.ReadTimeout,
WriteTimeout: s.p.conf.WriteTimeout, WriteTimeout: s.p.conf.WriteTimeout,
ReadBufferCount: 2, ReadBufferCount: 2,
@ -146,14 +146,14 @@ func (s *sourceRtsp) runInnerInner() bool {
return true return true
} }
_, err = conn.Options(s.path.conf.sourceUrl) _, err = conn.Options(s.path.conf.SourceUrl)
if err != nil { if err != nil {
conn.Close() conn.Close()
s.path.log("rtsp source ERR: %s", err) s.path.log("rtsp source ERR: %s", err)
return true return true
} }
tracks, _, err := conn.Describe(s.path.conf.sourceUrl) tracks, _, err := conn.Describe(s.path.conf.SourceUrl)
if err != nil { if err != nil {
conn.Close() conn.Close()
s.path.log("rtsp source ERR: %s", err) s.path.log("rtsp source ERR: %s", err)
@ -165,7 +165,7 @@ func (s *sourceRtsp) runInnerInner() bool {
s.path.sourceTrackCount = len(tracks) s.path.sourceTrackCount = len(tracks)
s.tracks = tracks s.tracks = tracks
if s.path.conf.sourceProtocolParsed == gortsplib.StreamProtocolUDP { if s.path.conf.SourceProtocolParsed == gortsplib.StreamProtocolUDP {
return s.runUDP(conn) return s.runUDP(conn)
} else { } else {
return s.runTCP(conn) return s.runTCP(conn)
@ -174,7 +174,7 @@ func (s *sourceRtsp) runInnerInner() bool {
func (s *sourceRtsp) runUDP(conn *gortsplib.ConnClient) bool { func (s *sourceRtsp) runUDP(conn *gortsplib.ConnClient) bool {
for _, track := range s.tracks { for _, track := range s.tracks {
_, err := conn.SetupUDP(s.path.conf.sourceUrl, gortsplib.TransportModePlay, track, 0, 0) _, err := conn.SetupUDP(s.path.conf.SourceUrl, gortsplib.TransportModePlay, track, 0, 0)
if err != nil { if err != nil {
conn.Close() conn.Close()
s.path.log("rtsp source ERR: %s", err) s.path.log("rtsp source ERR: %s", err)
@ -182,7 +182,7 @@ func (s *sourceRtsp) runUDP(conn *gortsplib.ConnClient) bool {
} }
} }
_, err := conn.Play(s.path.conf.sourceUrl) _, err := conn.Play(s.path.conf.SourceUrl)
if err != nil { if err != nil {
conn.Close() conn.Close()
s.path.log("rtsp source ERR: %s", err) s.path.log("rtsp source ERR: %s", err)
@ -264,7 +264,7 @@ outer:
func (s *sourceRtsp) runTCP(conn *gortsplib.ConnClient) bool { func (s *sourceRtsp) runTCP(conn *gortsplib.ConnClient) bool {
for _, track := range s.tracks { for _, track := range s.tracks {
_, err := conn.SetupTCP(s.path.conf.sourceUrl, gortsplib.TransportModePlay, track) _, err := conn.SetupTCP(s.path.conf.SourceUrl, gortsplib.TransportModePlay, track)
if err != nil { if err != nil {
conn.Close() conn.Close()
s.path.log("rtsp source ERR: %s", err) s.path.log("rtsp source ERR: %s", err)
@ -272,7 +272,7 @@ func (s *sourceRtsp) runTCP(conn *gortsplib.ConnClient) bool {
} }
} }
_, err := conn.Play(s.path.conf.sourceUrl) _, err := conn.Play(s.path.conf.SourceUrl)
if err != nil { if err != nil {
conn.Close() conn.Close()
s.path.log("rtsp source ERR: %s", err) s.path.log("rtsp source ERR: %s", err)

47
utils.go

@ -3,7 +3,6 @@ package main
import ( import (
"fmt" "fmt"
"net" "net"
"regexp"
"strings" "strings"
"sync" "sync"
@ -11,30 +10,6 @@ import (
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
) )
func parseIpCidrList(in []string) ([]interface{}, error) {
if len(in) == 0 {
return nil, nil
}
var ret []interface{}
for _, t := range in {
_, ipnet, err := net.ParseCIDR(t)
if err == nil {
ret = append(ret, ipnet)
continue
}
ip := net.ParseIP(t)
if ip != nil {
ret = append(ret, ip)
continue
}
return nil, fmt.Errorf("unable to parse ip/network '%s'", t)
}
return ret, nil
}
func ipEqualOrInRange(ip net.IP, ips []interface{}) bool { func ipEqualOrInRange(ip net.IP, ips []interface{}) bool {
for _, item := range ips { for _, item := range ips {
switch titem := item.(type) { switch titem := item.(type) {
@ -88,28 +63,6 @@ func removeQueryFromPath(path string) string {
return path return path
} }
var rePathName = regexp.MustCompile("^[0-9a-zA-Z_\\-/]+$")
func checkPathName(name string) error {
if name == "" {
return fmt.Errorf("cannot be empty")
}
if name[0] == '/' {
return fmt.Errorf("can't begin with a slash")
}
if name[len(name)-1] == '/' {
return fmt.Errorf("can't end with a slash")
}
if !rePathName.MatchString(name) {
return fmt.Errorf("can contain only alfanumeric characters, underscore, minus or slash")
}
return nil
}
type udpPublisherAddr struct { type udpPublisherAddr struct {
ip [net.IPv6len]byte // use a fixed-size array to enable the equality operator ip [net.IPv6len]byte // use a fixed-size array to enable the equality operator
port int port int

Loading…
Cancel
Save