From bbc2401ac2f1c818064533d6661edce10482e6ad Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Sun, 1 Nov 2020 17:33:06 +0100 Subject: [PATCH] new environment variable RTSP_PORT --- Makefile | 10 ++++----- README.md | 8 +++---- client/client.go | 44 +++++++++++++++++++++++--------------- clientman/clientman.go | 17 ++++++--------- externalcmd/externalcmd.go | 24 ++++++++++++++------- main.go | 13 ++++++----- main_test.go | 6 +++--- path/path.go | 16 ++++++++++---- pathman/pathman.go | 18 ++++++++-------- rtsp-simple-server.yml | 5 +++++ 10 files changed, 95 insertions(+), 66 deletions(-) diff --git a/Makefile b/Makefile index 3cc30338..8f6ed67e 100644 --- a/Makefile +++ b/Makefile @@ -80,19 +80,19 @@ define CONFIG_RUN paths: all: -# runOnPublish: ffmpeg -i rtsp://localhost:8554/$$RTSP_PATH -c copy -f mpegts myfile_$$RTSP_PATH.ts +# runOnPublish: ffmpeg -i rtsp://localhost:$$RTSP_PORT/$$RTSP_PATH -c copy -f mpegts myfile_$$RTSP_PATH.ts # readUser: test # readPass: tast -# runOnDemand: ffmpeg -re -stream_loop -1 -i testimages/ffmpeg/emptyvideo.ts -c copy -f rtsp rtsp://localhost:8554/$$RTSP_PATH +# runOnDemand: ffmpeg -re -stream_loop -1 -i testimages/ffmpeg/emptyvideo.ts -c copy -f rtsp rtsp://localhost:$$RTSP_PORT/$$RTSP_PATH # proxied: -# source: rtsp://192.168.2.198:8554/stream +# source: rtsp://192.168.2.198:554/stream # sourceProtocol: tcp # sourceOnDemand: yes -# runOnDemand: ffmpeg -i rtsp://192.168.2.198:8554/stream -c copy -f rtsp rtsp://localhost:8554/proxied2 +# runOnDemand: ffmpeg -i rtsp://192.168.2.198:554/stream -c copy -f rtsp rtsp://localhost:$$RTSP_PORT/proxied2 # original: -# runOnPublish: ffmpeg -i rtsp://localhost:8554/original -b:a 64k -c:v libx264 -preset ultrafast -b:v 500k -max_muxing_queue_size 1024 -f rtsp rtsp://localhost:8554/compressed +# runOnPublish: ffmpeg -i rtsp://localhost:554/original -b:a 64k -c:v libx264 -preset ultrafast -b:v 500k -max_muxing_queue_size 1024 -f rtsp rtsp://localhost:8554/compressed endef export CONFIG_RUN diff --git a/README.md b/README.md index e2c4faee..c15711c6 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Edit `rtsp-simple-server.yml` and replace everything inside section `paths` with ```yaml paths: cam: - runOnInit: ffmpeg -f v4l2 -i /dev/video0 -f rtsp rtsp://localhost:8554/$RTSP_PATH + runOnInit: ffmpeg -f v4l2 -i /dev/video0 -f rtsp rtsp://localhost:$RTSP_PORT/$RTSP_PATH runOnInitRestart: yes ``` @@ -128,7 +128,7 @@ Then edit `rtsp-simple-server.yml` and replace everything inside section `paths` ```yaml paths: cam: - runOnInit: gst-launch-1.0 rpicamsrc preview=false bitrate=2000000 keyframe-interval=50 ! video/x-h264,width=1920,height=1080,framerate=25/1 ! rtspclientsink location=rtsp://localhost:8554/$RTSP_PATH + runOnInit: gst-launch-1.0 rpicamsrc preview=false bitrate=2000000 keyframe-interval=50 ! video/x-h264,width=1920,height=1080,framerate=25/1 ! rtspclientsink location=rtsp://localhost:$RTSP_PORT/$RTSP_PATH runOnInitRestart: yes ``` @@ -140,7 +140,7 @@ Edit `rtsp-simple-server.yml` and replace everything inside section `paths` with ```yaml paths: ondemand: - runOnDemand: ffmpeg -re -stream_loop -1 -i file.ts -c copy -f rtsp rtsp://localhost:8554/$RTSP_PATH + runOnDemand: ffmpeg -re -stream_loop -1 -i file.ts -c copy -f rtsp rtsp://localhost:$RTSP_PORT/$RTSP_PATH runOnDemandRestart: yes ``` @@ -153,7 +153,7 @@ To change the format, codec or compression of a stream, you can use _FFmpeg_ or paths: all: original: - runOnPublish: ffmpeg -i rtsp://localhost:8554/$RTSP_PATH -b:a 64k -c:v libx264 -preset ultrafast -b:v 500k -max_muxing_queue_size 1024 -f rtsp rtsp://localhost:8554/compressed + runOnPublish: ffmpeg -i rtsp://localhost:$RTSP_PORT/$RTSP_PATH -b:a 64k -c:v libx264 -preset ultrafast -b:v 500k -max_muxing_queue_size 1024 -f rtsp rtsp://localhost:$RTSP_PORT/compressed runOnPublishRestart: yes ``` diff --git a/client/client.go b/client/client.go index 632e5147..146e7111 100644 --- a/client/client.go +++ b/client/client.go @@ -97,14 +97,15 @@ type Parent interface { } type Client struct { - wg *sync.WaitGroup - stats *stats.Stats - serverUdpRtp *serverudp.Server - serverUdpRtcp *serverudp.Server + rtspPort int readTimeout time.Duration runOnConnect string runOnConnectRestart bool protocols map[gortsplib.StreamProtocol]struct{} + wg *sync.WaitGroup + stats *stats.Stats + serverUdpRtp *serverudp.Server + serverUdpRtcp *serverudp.Server conn *gortsplib.ConnServer parent Parent @@ -128,27 +129,29 @@ type Client struct { } func New( - wg *sync.WaitGroup, - stats *stats.Stats, - serverUdpRtp *serverudp.Server, - serverUdpRtcp *serverudp.Server, + rtspPort int, readTimeout time.Duration, writeTimeout time.Duration, runOnConnect string, runOnConnectRestart bool, protocols map[gortsplib.StreamProtocol]struct{}, + wg *sync.WaitGroup, + stats *stats.Stats, + serverUdpRtp *serverudp.Server, + serverUdpRtcp *serverudp.Server, nconn net.Conn, parent Parent) *Client { c := &Client{ - wg: wg, - stats: stats, - serverUdpRtp: serverUdpRtp, - serverUdpRtcp: serverUdpRtcp, + rtspPort: rtspPort, readTimeout: readTimeout, runOnConnect: runOnConnect, runOnConnectRestart: runOnConnectRestart, protocols: protocols, + wg: wg, + stats: stats, + serverUdpRtp: serverUdpRtp, + serverUdpRtcp: serverUdpRtcp, conn: gortsplib.NewConnServer(gortsplib.ConnServerConf{ Conn: nconn, ReadTimeout: readTimeout, @@ -201,7 +204,10 @@ func (c *Client) run() { var onConnectCmd *externalcmd.ExternalCmd if c.runOnConnect != "" { - onConnectCmd = externalcmd.New(c.runOnConnect, c.runOnConnectRestart, "") + onConnectCmd = externalcmd.New(c.runOnConnect, c.runOnConnectRestart, externalcmd.Environment{ + Path: "", + Port: strconv.FormatInt(int64(c.rtspPort), 10), + }) } for { @@ -936,8 +942,10 @@ func (c *Client) runPlay() bool { var onReadCmd *externalcmd.ExternalCmd if c.path.Conf().RunOnRead != "" { - onReadCmd = externalcmd.New(c.path.Conf().RunOnRead, - c.path.Conf().RunOnReadRestart, c.path.Name()) + onReadCmd = externalcmd.New(c.path.Conf().RunOnRead, c.path.Conf().RunOnReadRestart, externalcmd.Environment{ + Path: c.path.Name(), + Port: strconv.FormatInt(int64(c.rtspPort), 10), + }) } if c.streamProtocol == gortsplib.StreamProtocolUDP { @@ -1111,8 +1119,10 @@ func (c *Client) runRecord() bool { var onPublishCmd *externalcmd.ExternalCmd if c.path.Conf().RunOnPublish != "" { - onPublishCmd = externalcmd.New(c.path.Conf().RunOnPublish, - c.path.Conf().RunOnPublishRestart, c.path.Name()) + onPublishCmd = externalcmd.New(c.path.Conf().RunOnPublish, c.path.Conf().RunOnPublishRestart, externalcmd.Environment{ + Path: c.path.Name(), + Port: strconv.FormatInt(int64(c.rtspPort), 10), + }) } if c.streamProtocol == gortsplib.StreamProtocolUDP { diff --git a/clientman/clientman.go b/clientman/clientman.go index ae5a1dae..4278a29c 100644 --- a/clientman/clientman.go +++ b/clientman/clientman.go @@ -20,6 +20,7 @@ type Parent interface { } type ClientManager struct { + rtspPort int readTimeout time.Duration writeTimeout time.Duration runOnConnect string @@ -44,6 +45,7 @@ type ClientManager struct { } func New( + rtspPort int, readTimeout time.Duration, writeTimeout time.Duration, runOnConnect string, @@ -57,6 +59,7 @@ func New( parent Parent) *ClientManager { cm := &ClientManager{ + rtspPort: rtspPort, readTimeout: readTimeout, writeTimeout: writeTimeout, runOnConnect: runOnConnect, @@ -94,17 +97,9 @@ outer: for { select { case conn := <-cm.serverTcp.Accept(): - c := client.New(&cm.wg, - cm.stats, - cm.serverUdpRtp, - cm.serverUdpRtcp, - cm.readTimeout, - cm.writeTimeout, - cm.runOnConnect, - cm.runOnConnectRestart, - cm.protocols, - conn, - cm) + c := client.New(cm.rtspPort, cm.readTimeout, cm.writeTimeout, + cm.runOnConnect, cm.runOnConnectRestart, cm.protocols, &cm.wg, + cm.stats, cm.serverUdpRtp, cm.serverUdpRtcp, conn, cm) cm.clients[c] = struct{}{} case c := <-cm.pathMan.ClientClose(): diff --git a/externalcmd/externalcmd.go b/externalcmd/externalcmd.go index 6659f999..3fe8ce72 100644 --- a/externalcmd/externalcmd.go +++ b/externalcmd/externalcmd.go @@ -12,10 +12,15 @@ const ( retryPause = 5 * time.Second ) +type Environment struct { + Path string + Port string +} + type ExternalCmd struct { - cmdstr string - restart bool - pathName string + cmdstr string + restart bool + env Environment // in terminate chan struct{} @@ -24,11 +29,11 @@ type ExternalCmd struct { done chan struct{} } -func New(cmdstr string, restart bool, pathName string) *ExternalCmd { +func New(cmdstr string, restart bool, env Environment) *ExternalCmd { e := &ExternalCmd{ cmdstr: cmdstr, restart: restart, - pathName: pathName, + env: env, terminate: make(chan struct{}), done: make(chan struct{}), } @@ -79,16 +84,19 @@ func (e *ExternalCmd) runInner() bool { // on Windows the shell is not used and command is started directly // variables are replaced manually in order to guarantee compatibility // with Linux commands - args := strings.Fields(strings.ReplaceAll(e.cmdstr, "$RTSP_PATH", e.pathName)) + tmp := strings.ReplaceAll(e.cmdstr, "$RTSP_PATH", e.env.Path) + tmp = strings.ReplaceAll(tmp, "$RTSP_PORT", e.env.Port) + + args := strings.Fields(tmp) cmd = exec.Command(args[0], args[1:]...) } else { cmd = exec.Command("/bin/sh", "-c", "exec "+e.cmdstr) } - // variables are inserted into the environment cmd.Env = append(os.Environ(), - "RTSP_PATH="+e.pathName, + "RTSP_PATH="+e.env.Path, + "RTSP_PORT="+e.env.Port, ) cmd.Stdout = os.Stdout diff --git a/main.go b/main.go index f8e39efd..e8c64619 100644 --- a/main.go +++ b/main.go @@ -175,13 +175,14 @@ func (p *program) createResources(initial bool) error { } if p.pathMan == nil { - p.pathMan = pathman.New(p.conf.ReadTimeout, p.conf.WriteTimeout, - p.conf.AuthMethodsParsed, p.conf.Paths, p.stats, p) + p.pathMan = pathman.New(p.conf.RtspPort, p.conf.ReadTimeout, + p.conf.WriteTimeout, p.conf.AuthMethodsParsed, p.conf.Paths, + p.stats, p) } if p.clientMan == nil { - p.clientMan = clientman.New(p.conf.ReadTimeout, p.conf.WriteTimeout, - p.conf.RunOnConnect, p.conf.RunOnConnectRestart, + p.clientMan = clientman.New(p.conf.RtspPort, p.conf.ReadTimeout, + p.conf.WriteTimeout, p.conf.RunOnConnect, p.conf.RunOnConnectRestart, p.conf.ProtocolsParsed, p.stats, p.serverUdpRtp, p.serverUdpRtcp, p.pathMan, p.serverTcp, p) } @@ -280,7 +281,8 @@ func (p *program) reloadConf() error { } closePathMan := false - if conf.ReadTimeout != p.conf.ReadTimeout || + if conf.RtspPort != p.conf.RtspPort || + conf.ReadTimeout != p.conf.ReadTimeout || conf.WriteTimeout != p.conf.WriteTimeout || !reflect.DeepEqual(conf.AuthMethodsParsed, p.conf.AuthMethodsParsed) { closePathMan = true @@ -293,6 +295,7 @@ func (p *program) reloadConf() error { closeServerUdpRtcp || closeServerTcp || closePathMan || + conf.RtspPort != p.conf.RtspPort || conf.ReadTimeout != p.conf.ReadTimeout || conf.WriteTimeout != p.conf.WriteTimeout || conf.RunOnConnect != p.conf.RunOnConnect || diff --git a/main_test.go b/main_test.go index 8ac95664..2f7633f7 100644 --- a/main_test.go +++ b/main_test.go @@ -692,7 +692,7 @@ func TestRedirect(t *testing.T) { func TestRunOnDemand(t *testing.T) { p1, err := testProgram("paths:\n" + " all:\n" + - " runOnDemand: ffmpeg -hide_banner -loglevel error -re -i testimages/ffmpeg/emptyvideo.ts -c copy -f rtsp rtsp://localhost:8554/$RTSP_PATH\n") + " runOnDemand: ffmpeg -hide_banner -loglevel error -re -i testimages/ffmpeg/emptyvideo.ts -c copy -f rtsp rtsp://localhost:$RTSP_PORT/$RTSP_PATH\n") require.NoError(t, err) defer p1.close() @@ -716,7 +716,7 @@ func TestHotReloading(t *testing.T) { err := ioutil.WriteFile(confPath, []byte("paths:\n"+ " test1:\n"+ - " runOnDemand: ffmpeg -hide_banner -loglevel error -re -i testimages/ffmpeg/emptyvideo.ts -c copy -f rtsp rtsp://localhost:8554/$RTSP_PATH\n"), + " runOnDemand: ffmpeg -hide_banner -loglevel error -re -i testimages/ffmpeg/emptyvideo.ts -c copy -f rtsp rtsp://localhost:$RTSP_PORT/$RTSP_PATH\n"), 0644) require.NoError(t, err) @@ -742,7 +742,7 @@ func TestHotReloading(t *testing.T) { err = ioutil.WriteFile(confPath, []byte("paths:\n"+ " test2:\n"+ - " runOnDemand: ffmpeg -hide_banner -loglevel error -re -i testimages/ffmpeg/emptyvideo.ts -c copy -f rtsp rtsp://localhost:8554/$RTSP_PATH\n"), + " runOnDemand: ffmpeg -hide_banner -loglevel error -re -i testimages/ffmpeg/emptyvideo.ts -c copy -f rtsp rtsp://localhost:$RTSP_PORT/$RTSP_PATH\n"), 0644) require.NoError(t, err) diff --git a/path/path.go b/path/path.go index 6c5736df..f8635467 100644 --- a/path/path.go +++ b/path/path.go @@ -2,6 +2,7 @@ package path import ( "fmt" + "strconv" "strings" "sync" "sync/atomic" @@ -125,6 +126,7 @@ const ( ) type Path struct { + rtspPort int readTimeout time.Duration writeTimeout time.Duration confName string @@ -165,6 +167,7 @@ type Path struct { } func New( + rtspPort int, readTimeout time.Duration, writeTimeout time.Duration, confName string, @@ -175,6 +178,7 @@ func New( parent Parent) *Path { pa := &Path{ + rtspPort: rtspPort, readTimeout: readTimeout, writeTimeout: writeTimeout, confName: confName, @@ -225,8 +229,10 @@ func (pa *Path) run() { if pa.conf.RunOnInit != "" { pa.Log("on init command started") - pa.onInitCmd = externalcmd.New(pa.conf.RunOnInit, - pa.conf.RunOnInitRestart, pa.name) + pa.onInitCmd = externalcmd.New(pa.conf.RunOnInit, pa.conf.RunOnInitRestart, externalcmd.Environment{ + Path: pa.name, + Port: strconv.FormatInt(int64(pa.rtspPort), 10), + }) } outer: @@ -575,8 +581,10 @@ func (pa *Path) onClientDescribe(c *client.Client) { if pa.conf.RunOnDemand != "" { if pa.onDemandCmd == nil { pa.Log("on demand command started") - pa.onDemandCmd = externalcmd.New(pa.conf.RunOnDemand, - pa.conf.RunOnDemandRestart, pa.name) + pa.onDemandCmd = externalcmd.New(pa.conf.RunOnDemand, pa.conf.RunOnDemandRestart, externalcmd.Environment{ + Path: pa.name, + Port: strconv.FormatInt(int64(pa.rtspPort), 10), + }) if pa.sourceState != sourceStateWaitingDescribe { pa.describeTimer = time.NewTimer(pa.conf.RunOnDemandStartTimeout) diff --git a/pathman/pathman.go b/pathman/pathman.go index d4317f84..789c344f 100644 --- a/pathman/pathman.go +++ b/pathman/pathman.go @@ -20,6 +20,7 @@ type Parent interface { } type PathManager struct { + rtspPort int readTimeout time.Duration writeTimeout time.Duration authMethods []headers.AuthMethod @@ -44,6 +45,7 @@ type PathManager struct { } func New( + rtspPort int, readTimeout time.Duration, writeTimeout time.Duration, authMethods []headers.AuthMethod, @@ -52,6 +54,7 @@ func New( parent Parent) *PathManager { pm := &PathManager{ + rtspPort: rtspPort, readTimeout: readTimeout, writeTimeout: writeTimeout, authMethods: authMethods, @@ -155,9 +158,8 @@ outer: // create path if it doesn't exist if _, ok := pm.paths[req.PathName]; !ok { - pa := path.New( - pm.readTimeout, pm.writeTimeout, pathName, pathConf, req.PathName, - &pm.wg, pm.stats, pm) + pa := path.New(pm.rtspPort, pm.readTimeout, pm.writeTimeout, + pathName, pathConf, req.PathName, &pm.wg, pm.stats, pm) pm.paths[req.PathName] = pa } @@ -179,9 +181,8 @@ outer: // create path if it doesn't exist if _, ok := pm.paths[req.PathName]; !ok { - pa := path.New( - pm.readTimeout, pm.writeTimeout, pathName, pathConf, req.PathName, - &pm.wg, pm.stats, pm) + pa := path.New(pm.rtspPort, pm.readTimeout, pm.writeTimeout, + pathName, pathConf, req.PathName, &pm.wg, pm.stats, pm) pm.paths[req.PathName] = pa } @@ -254,9 +255,8 @@ outer: func (pm *PathManager) createPaths() { for pathName, pathConf := range pm.pathConfs { if pathConf.Regexp == nil { - pa := path.New( - pm.readTimeout, pm.writeTimeout, pathName, pathConf, pathName, - &pm.wg, pm.stats, pm) + pa := path.New(pm.rtspPort, pm.readTimeout, pm.writeTimeout, + pathName, pathConf, pathName, &pm.wg, pm.stats, pm) pm.paths[pathName] = pa } } diff --git a/rtsp-simple-server.yml b/rtsp-simple-server.yml index 3acb7b21..6115a37f 100644 --- a/rtsp-simple-server.yml +++ b/rtsp-simple-server.yml @@ -29,6 +29,7 @@ logFile: rtsp-simple-server.log # command to run when a client connects to the server. # this is terminated with SIGINT when a client disconnects from the server. +# the server port is available in the RTSP_PORT variable. # the restart parameter allows to restart the command if it exits suddenly. runOnConnect: runOnConnectRestart: no @@ -83,6 +84,7 @@ paths: # this can be used to publish a stream and keep it always opened. # this is terminated with SIGINT when the program closes. # the path name is available in the RTSP_PATH variable. + # the server port is available in the RTSP_PORT variable. # the restart parameter allows to restart the command if it exits suddenly. runOnInit: runOnInitRestart: no @@ -91,6 +93,7 @@ paths: # this can be used to publish a stream on demand. # this is terminated with SIGINT when the path is not requested anymore. # the path name is available in the RTSP_PATH variable. + # the server port is available in the RTSP_PORT variable. # the restart parameter allows to restart the command if it exits suddenly. runOnDemand: runOnDemandRestart: no @@ -104,6 +107,7 @@ paths: # command to run when a client starts publishing. # this is terminated with SIGINT when a client stops publishing. # the path name is available in the RTSP_PATH variable. + # the server port is available in the RTSP_PORT variable. # the restart parameter allows to restart the command if it exits suddenly. runOnPublish: runOnPublishRestart: no @@ -111,6 +115,7 @@ paths: # command to run when a clients starts reading. # this is terminated with SIGINT when a client stops reading. # the path name is available in the RTSP_PATH variable. + # the server port is available in the RTSP_PORT variable. # the restart parameter allows to restart the command if it exits suddenly. runOnRead: runOnReadRestart: no