From a9dee11496a1d48f3fb910555e66f1d06814b872 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Sat, 21 Nov 2020 13:34:27 +0100 Subject: [PATCH] support automatic protocol selection when proxying rtsp --- go.mod | 2 +- go.sum | 4 +-- internal/client/client.go | 8 ++--- internal/conf/pathconf.go | 64 +++++++++++++++++++---------------- internal/path/path.go | 8 ++--- internal/sourcertsp/source.go | 4 +-- main_test.go | 13 ++++--- rtsp-simple-server.yml | 4 +-- 8 files changed, 55 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index 35d738dd..e0b2fde1 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.15 require ( github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect - github.com/aler9/gortsplib v0.0.0-20201120083135-e66459731e97 + github.com/aler9/gortsplib v0.0.0-20201121121323-a6d0fc140b6e github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.4.9 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 diff --git a/go.sum b/go.sum index f7871939..06be6b62 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/aler9/gortsplib v0.0.0-20201120083135-e66459731e97 h1:sefesnUXzUHF4fhS+rnpON5MpMOjka5YFka9P5qiS5s= -github.com/aler9/gortsplib v0.0.0-20201120083135-e66459731e97/go.mod h1:6yKsTNIrCapRz90WHQtyFV/rKK0TT+QapxUXNqSJi9M= +github.com/aler9/gortsplib v0.0.0-20201121121323-a6d0fc140b6e h1:4BltbY0GVgVQntou5KQGpE0fL33tRq5o1zPY5pbyrlU= +github.com/aler9/gortsplib v0.0.0-20201121121323-a6d0fc140b6e/go.mod h1:6yKsTNIrCapRz90WHQtyFV/rKK0TT+QapxUXNqSJi9M= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/internal/client/client.go b/internal/client/client.go index 57e7805c..c9860ef3 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -205,12 +205,12 @@ func (c *Client) run() { defer c.wg.Done() defer c.log("disconnected") - var onConnectCmd *externalcmd.ExternalCmd if c.runOnConnect != "" { - onConnectCmd = externalcmd.New(c.runOnConnect, c.runOnConnectRestart, externalcmd.Environment{ + onConnectCmd := externalcmd.New(c.runOnConnect, c.runOnConnectRestart, externalcmd.Environment{ Path: "", Port: strconv.FormatInt(int64(c.rtspPort), 10), }) + defer onConnectCmd.Close() } for { @@ -223,10 +223,6 @@ func (c *Client) run() { c.path.OnClientRemove(c) c.path = nil } - - if onConnectCmd != nil { - onConnectCmd.Close() - } } type errAuthNotCritical struct { diff --git a/internal/conf/pathconf.go b/internal/conf/pathconf.go index b8c84eb2..4b15544b 100644 --- a/internal/conf/pathconf.go +++ b/internal/conf/pathconf.go @@ -18,33 +18,33 @@ const userPassSupportedChars = "A-Z,0-9,!,$,(,),*,+,.,;,<,=,>,[,],^,_,-,{,}" // PathConf is a path configuration. type PathConf struct { - Regexp *regexp.Regexp `yaml:"-" json:"-"` - Source string `yaml:"source"` - SourceProtocol string `yaml:"sourceProtocol"` - SourceProtocolParsed gortsplib.StreamProtocol `yaml:"-" json:"-"` - SourceOnDemand bool `yaml:"sourceOnDemand"` - SourceOnDemandStartTimeout time.Duration `yaml:"sourceOnDemandStartTimeout"` - SourceOnDemandCloseAfter time.Duration `yaml:"sourceOnDemandCloseAfter"` - SourceRedirect string `yaml:"sourceRedirect"` - Fallback string `yaml:"fallback"` - RunOnInit string `yaml:"runOnInit"` - RunOnInitRestart bool `yaml:"runOnInitRestart"` - RunOnDemand string `yaml:"runOnDemand"` - RunOnDemandRestart bool `yaml:"runOnDemandRestart"` - RunOnDemandStartTimeout time.Duration `yaml:"runOnDemandStartTimeout"` - RunOnDemandCloseAfter time.Duration `yaml:"runOnDemandCloseAfter"` - RunOnPublish string `yaml:"runOnPublish"` - RunOnPublishRestart bool `yaml:"runOnPublishRestart"` - RunOnRead string `yaml:"runOnRead"` - RunOnReadRestart bool `yaml:"runOnReadRestart"` - PublishUser string `yaml:"publishUser"` - PublishPass string `yaml:"publishPass"` - PublishIps []string `yaml:"publishIps"` - PublishIpsParsed []interface{} `yaml:"-" json:"-"` - ReadUser string `yaml:"readUser"` - ReadPass string `yaml:"readPass"` - ReadIps []string `yaml:"readIps"` - ReadIpsParsed []interface{} `yaml:"-" json:"-"` + Regexp *regexp.Regexp `yaml:"-" json:"-"` + Source string `yaml:"source"` + SourceProtocol string `yaml:"sourceProtocol"` + SourceProtocolParsed *gortsplib.StreamProtocol `yaml:"-" json:"-"` + SourceOnDemand bool `yaml:"sourceOnDemand"` + SourceOnDemandStartTimeout time.Duration `yaml:"sourceOnDemandStartTimeout"` + SourceOnDemandCloseAfter time.Duration `yaml:"sourceOnDemandCloseAfter"` + SourceRedirect string `yaml:"sourceRedirect"` + Fallback string `yaml:"fallback"` + RunOnInit string `yaml:"runOnInit"` + RunOnInitRestart bool `yaml:"runOnInitRestart"` + RunOnDemand string `yaml:"runOnDemand"` + RunOnDemandRestart bool `yaml:"runOnDemandRestart"` + RunOnDemandStartTimeout time.Duration `yaml:"runOnDemandStartTimeout"` + RunOnDemandCloseAfter time.Duration `yaml:"runOnDemandCloseAfter"` + RunOnPublish string `yaml:"runOnPublish"` + RunOnPublishRestart bool `yaml:"runOnPublishRestart"` + RunOnRead string `yaml:"runOnRead"` + RunOnReadRestart bool `yaml:"runOnReadRestart"` + PublishUser string `yaml:"publishUser"` + PublishPass string `yaml:"publishPass"` + PublishIps []string `yaml:"publishIps"` + PublishIpsParsed []interface{} `yaml:"-" json:"-"` + ReadUser string `yaml:"readUser"` + ReadPass string `yaml:"readPass"` + ReadIps []string `yaml:"readIps"` + ReadIpsParsed []interface{} `yaml:"-" json:"-"` } func (pconf *PathConf) fillAndCheck(name string) error { @@ -94,15 +94,19 @@ func (pconf *PathConf) fillAndCheck(name string) error { } if pconf.SourceProtocol == "" { - pconf.SourceProtocol = "udp" + pconf.SourceProtocol = "automatic" } switch pconf.SourceProtocol { case "udp": - pconf.SourceProtocolParsed = gortsplib.StreamProtocolUDP + v := gortsplib.StreamProtocolUDP + pconf.SourceProtocolParsed = &v case "tcp": - pconf.SourceProtocolParsed = gortsplib.StreamProtocolTCP + v := gortsplib.StreamProtocolTCP + pconf.SourceProtocolParsed = &v + + case "automatic": default: return fmt.Errorf("unsupported protocol '%s'", pconf.SourceProtocol) diff --git a/internal/path/path.go b/internal/path/path.go index 32e78dd1..d9aa8f4f 100644 --- a/internal/path/path.go +++ b/internal/path/path.go @@ -155,7 +155,6 @@ type Path struct { sourceTrackCount int sourceSdp []byte readers *readersMap - onInitCmd *externalcmd.ExternalCmd onDemandCmd *externalcmd.ExternalCmd describeTimer *time.Timer sourceCloseTimer *time.Timer @@ -245,9 +244,10 @@ func (pa *Path) run() { pa.startExternalSource() } + var onInitCmd *externalcmd.ExternalCmd if pa.conf.RunOnInit != "" { pa.Log("on init command started") - pa.onInitCmd = externalcmd.New(pa.conf.RunOnInit, pa.conf.RunOnInitRestart, externalcmd.Environment{ + onInitCmd = externalcmd.New(pa.conf.RunOnInit, pa.conf.RunOnInitRestart, externalcmd.Environment{ Path: pa.name, Port: strconv.FormatInt(int64(pa.rtspPort), 10), }) @@ -363,9 +363,9 @@ outer: pa.runOnDemandCloseTimer.Stop() pa.closeTimer.Stop() - if pa.onInitCmd != nil { + if onInitCmd != nil { pa.Log("on init command stopped") - pa.onInitCmd.Close() + onInitCmd.Close() } if source, ok := pa.source.(sourceExternal); ok { diff --git a/internal/sourcertsp/source.go b/internal/sourcertsp/source.go index f76801b9..be702559 100644 --- a/internal/sourcertsp/source.go +++ b/internal/sourcertsp/source.go @@ -25,7 +25,7 @@ type Parent interface { // Source is a RTSP source. type Source struct { ur string - proto gortsplib.StreamProtocol + proto *gortsplib.StreamProtocol readTimeout time.Duration writeTimeout time.Duration wg *sync.WaitGroup @@ -41,7 +41,7 @@ type Source struct { // New allocates a Source. func New(ur string, - proto gortsplib.StreamProtocol, + proto *gortsplib.StreamProtocol, readTimeout time.Duration, writeTimeout time.Duration, wg *sync.WaitGroup, diff --git a/main_test.go b/main_test.go index 4a4dada8..d60e3541 100644 --- a/main_test.go +++ b/main_test.go @@ -187,7 +187,7 @@ func TestEnvironment(t *testing.T) { require.Equal(t, &conf.PathConf{ Regexp: regexp.MustCompile("^.*$"), Source: "record", - SourceProtocol: "udp", + SourceProtocol: "automatic", SourceOnDemandStartTimeout: 10 * time.Second, SourceOnDemandCloseAfter: 10 * time.Second, ReadUser: "testuser", @@ -199,9 +199,12 @@ func TestEnvironment(t *testing.T) { pa, ok = p.conf.Paths["cam1"] require.Equal(t, true, ok) require.Equal(t, &conf.PathConf{ - Source: "rtsp://testing", - SourceProtocol: "tcp", - SourceProtocolParsed: gortsplib.StreamProtocolTCP, + Source: "rtsp://testing", + SourceProtocol: "tcp", + SourceProtocolParsed: func() *gortsplib.StreamProtocol { + v := gortsplib.StreamProtocolTCP + return &v + }(), SourceOnDemand: true, SourceOnDemandStartTimeout: 10 * time.Second, SourceOnDemandCloseAfter: 10 * time.Second, @@ -222,7 +225,7 @@ func TestEnvironmentNoFile(t *testing.T) { require.Equal(t, true, ok) require.Equal(t, &conf.PathConf{ Source: "rtsp://testing", - SourceProtocol: "udp", + SourceProtocol: "automatic", SourceOnDemandStartTimeout: 10 * time.Second, SourceOnDemandCloseAfter: 10 * time.Second, RunOnDemandStartTimeout: 10 * time.Second, diff --git a/rtsp-simple-server.yml b/rtsp-simple-server.yml index 15dc7858..097f5a6d 100644 --- a/rtsp-simple-server.yml +++ b/rtsp-simple-server.yml @@ -48,8 +48,8 @@ paths: source: record # if the source is an RTSP url, this is the protocol that will be used to - # pull the stream. - sourceProtocol: udp + # pull the stream. available options are "automatic", "udp", "tcp". + sourceProtocol: automatic # if the source is an RTSP or RTMP url, it will be pulled only when at least # one reader is connected, saving bandwidth. sourceOnDemand: no