mirror of
https://github.com/bluenviron/mediamtx
synced 2025-03-04 19:37:52 +00:00
add runOnUnDemand hook (#2645)
This commit is contained in:
parent
1d1d64cb89
commit
813611057d
36
README.md
36
README.md
@ -1311,20 +1311,28 @@ paths:
|
||||
`runOnDemand` allows to run a command when a path is requested by a reader. This can be used to publish a stream on demand:
|
||||
|
||||
```yml
|
||||
paths:
|
||||
mypath:
|
||||
# Command to run when this path is requested by a reader
|
||||
# and no one is publishing to this path yet.
|
||||
# This is terminated with SIGINT when the program closes.
|
||||
# The following environment variables are available:
|
||||
# * MTX_PATH: path name
|
||||
# * MTX_QUERY: query parameters (passed by first reader)
|
||||
# * RTSP_PORT: RTSP server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnDemand: ffmpeg -i my_file.mp4 -c copy -f rtsp rtsp://localhost:8554/mypath
|
||||
# Restart the command if it exits.
|
||||
runOnDemandRestart: no
|
||||
pathDefaults:
|
||||
# Command to run when this path is requested by a reader
|
||||
# and no one is publishing to this path yet.
|
||||
# This is terminated with SIGINT when there are no readers anymore.
|
||||
# The following environment variables are available:
|
||||
# * MTX_PATH: path name
|
||||
# * MTX_QUERY: query parameters (passed by first reader)
|
||||
# * RTSP_PORT: RTSP server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnDemand: ffmpeg -i my_file.mp4 -c copy -f rtsp rtsp://localhost:8554/mypath
|
||||
# Restart the command if it exits.
|
||||
runOnDemandRestart: no
|
||||
```
|
||||
|
||||
`runOnUnDemand` allows to run a command when there are no readers anymore:
|
||||
|
||||
```yml
|
||||
pathDefaults:
|
||||
# Command to run when there are no readers anymore.
|
||||
# Environment variables are the same of runOnDemand.
|
||||
runOnUnDemand:
|
||||
```
|
||||
|
||||
`runOnReady` allows to run a command when a stream is ready to be read:
|
||||
|
@ -344,6 +344,8 @@ components:
|
||||
type: string
|
||||
runOnDemandCloseAfter:
|
||||
type: string
|
||||
runOnUnDemand:
|
||||
type: string
|
||||
runOnReady:
|
||||
type: string
|
||||
runOnReadyRestart:
|
||||
|
@ -135,6 +135,7 @@ type Path struct {
|
||||
RunOnDemandRestart bool `json:"runOnDemandRestart"`
|
||||
RunOnDemandStartTimeout StringDuration `json:"runOnDemandStartTimeout"`
|
||||
RunOnDemandCloseAfter StringDuration `json:"runOnDemandCloseAfter"`
|
||||
RunOnUnDemand string `json:"runOnUnDemand"`
|
||||
RunOnReady string `json:"runOnReady"`
|
||||
RunOnReadyRestart bool `json:"runOnReadyRestart"`
|
||||
RunOnNotReady string `json:"runOnNotReady"`
|
||||
@ -489,8 +490,8 @@ func (pconf *Path) check(conf *Conf, name string) error {
|
||||
return fmt.Errorf("a path with a regular expression (or path 'all')" +
|
||||
" does not support option 'runOnInit'; use another path")
|
||||
}
|
||||
if pconf.RunOnDemand != "" && pconf.Source != "publisher" {
|
||||
return fmt.Errorf("'runOnDemand' can be used only when source is 'publisher'")
|
||||
if (pconf.RunOnDemand != "" || pconf.RunOnUnDemand != "") && pconf.Source != "publisher" {
|
||||
return fmt.Errorf("'runOnDemand' and 'runOnUnDemand' can be used only when source is 'publisher'")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1,8 +1,6 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/defs"
|
||||
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
||||
"github.com/bluenviron/mediamtx/internal/logger"
|
||||
@ -16,7 +14,7 @@ type conn struct {
|
||||
externalCmdPool *externalcmd.Pool
|
||||
logger logger.Writer
|
||||
|
||||
onConnectCmd *externalcmd.Cmd
|
||||
onDisconnectHook func()
|
||||
}
|
||||
|
||||
func newConn(
|
||||
@ -38,48 +36,9 @@ func newConn(
|
||||
}
|
||||
|
||||
func (c *conn) open(desc defs.APIPathSourceOrReader) {
|
||||
if c.runOnConnect != "" {
|
||||
c.logger.Log(logger.Info, "runOnConnect command started")
|
||||
|
||||
_, port, _ := net.SplitHostPort(c.rtspAddress)
|
||||
env := externalcmd.Environment{
|
||||
"RTSP_PORT": port,
|
||||
"MTX_CONN_TYPE": desc.Type,
|
||||
"MTX_CONN_ID": desc.ID,
|
||||
}
|
||||
|
||||
c.onConnectCmd = externalcmd.NewCmd(
|
||||
c.externalCmdPool,
|
||||
c.runOnConnect,
|
||||
c.runOnConnectRestart,
|
||||
env,
|
||||
func(err error) {
|
||||
c.logger.Log(logger.Info, "runOnConnect command exited: %v", err)
|
||||
})
|
||||
}
|
||||
c.onDisconnectHook = onConnectHook(c, desc)
|
||||
}
|
||||
|
||||
func (c *conn) close(desc defs.APIPathSourceOrReader) {
|
||||
if c.onConnectCmd != nil {
|
||||
c.onConnectCmd.Close()
|
||||
c.logger.Log(logger.Info, "runOnConnect command stopped")
|
||||
}
|
||||
|
||||
if c.runOnDisconnect != "" {
|
||||
c.logger.Log(logger.Info, "runOnDisconnect command launched")
|
||||
|
||||
_, port, _ := net.SplitHostPort(c.rtspAddress)
|
||||
env := externalcmd.Environment{
|
||||
"RTSP_PORT": port,
|
||||
"MTX_CONN_TYPE": desc.Type,
|
||||
"MTX_CONN_ID": desc.ID,
|
||||
}
|
||||
|
||||
externalcmd.NewCmd(
|
||||
c.externalCmdPool,
|
||||
c.runOnDisconnect,
|
||||
false,
|
||||
env,
|
||||
nil)
|
||||
}
|
||||
func (c *conn) close() {
|
||||
c.onDisconnectHook()
|
||||
}
|
||||
|
208
internal/core/hooks.go
Normal file
208
internal/core/hooks.go
Normal file
@ -0,0 +1,208 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/conf"
|
||||
"github.com/bluenviron/mediamtx/internal/defs"
|
||||
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
||||
"github.com/bluenviron/mediamtx/internal/logger"
|
||||
)
|
||||
|
||||
func onInitHook(path *path) func() {
|
||||
var onInitCmd *externalcmd.Cmd
|
||||
|
||||
if path.conf.RunOnInit != "" {
|
||||
path.Log(logger.Info, "runOnInit command started")
|
||||
onInitCmd = externalcmd.NewCmd(
|
||||
path.externalCmdPool,
|
||||
path.conf.RunOnInit,
|
||||
path.conf.RunOnInitRestart,
|
||||
path.externalCmdEnv(),
|
||||
func(err error) {
|
||||
path.Log(logger.Info, "runOnInit command exited: %v", err)
|
||||
})
|
||||
}
|
||||
|
||||
return func() {
|
||||
if onInitCmd != nil {
|
||||
onInitCmd.Close()
|
||||
path.Log(logger.Info, "runOnInit command stopped")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func onConnectHook(c *conn, desc defs.APIPathSourceOrReader) func() {
|
||||
var env externalcmd.Environment
|
||||
var onConnectCmd *externalcmd.Cmd
|
||||
|
||||
if c.runOnConnect != "" || c.runOnDisconnect != "" {
|
||||
_, port, _ := net.SplitHostPort(c.rtspAddress)
|
||||
env = externalcmd.Environment{
|
||||
"RTSP_PORT": port,
|
||||
"MTX_CONN_TYPE": desc.Type,
|
||||
"MTX_CONN_ID": desc.ID,
|
||||
}
|
||||
}
|
||||
|
||||
if c.runOnConnect != "" {
|
||||
c.logger.Log(logger.Info, "runOnConnect command started")
|
||||
|
||||
onConnectCmd = externalcmd.NewCmd(
|
||||
c.externalCmdPool,
|
||||
c.runOnConnect,
|
||||
c.runOnConnectRestart,
|
||||
env,
|
||||
func(err error) {
|
||||
c.logger.Log(logger.Info, "runOnConnect command exited: %v", err)
|
||||
})
|
||||
}
|
||||
|
||||
return func() {
|
||||
if onConnectCmd != nil {
|
||||
onConnectCmd.Close()
|
||||
c.logger.Log(logger.Info, "runOnConnect command stopped")
|
||||
}
|
||||
|
||||
if c.runOnDisconnect != "" {
|
||||
c.logger.Log(logger.Info, "runOnDisconnect command launched")
|
||||
externalcmd.NewCmd(
|
||||
c.externalCmdPool,
|
||||
c.runOnDisconnect,
|
||||
false,
|
||||
env,
|
||||
nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func onDemandHook(path *path, query string) func(string) {
|
||||
var env externalcmd.Environment
|
||||
var onDemandCmd *externalcmd.Cmd
|
||||
|
||||
if path.conf.RunOnDemand != "" || path.conf.RunOnUnDemand != "" {
|
||||
env = path.externalCmdEnv()
|
||||
env["MTX_QUERY"] = query
|
||||
}
|
||||
|
||||
if path.conf.RunOnDemand != "" {
|
||||
path.Log(logger.Info, "runOnDemand command started")
|
||||
|
||||
onDemandCmd = externalcmd.NewCmd(
|
||||
path.externalCmdPool,
|
||||
path.conf.RunOnDemand,
|
||||
path.conf.RunOnDemandRestart,
|
||||
env,
|
||||
func(err error) {
|
||||
path.Log(logger.Info, "runOnDemand command exited: %v", err)
|
||||
})
|
||||
}
|
||||
|
||||
return func(reason string) {
|
||||
if onDemandCmd != nil {
|
||||
onDemandCmd.Close()
|
||||
path.Log(logger.Info, "runOnDemand command stopped: %v", reason)
|
||||
}
|
||||
|
||||
if path.conf.RunOnUnDemand != "" {
|
||||
path.Log(logger.Info, "runOnUnDemand command launched")
|
||||
externalcmd.NewCmd(
|
||||
path.externalCmdPool,
|
||||
path.conf.RunOnUnDemand,
|
||||
false,
|
||||
env,
|
||||
nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func onReadyHook(path *path) func() {
|
||||
var env externalcmd.Environment
|
||||
var onReadyCmd *externalcmd.Cmd
|
||||
|
||||
if path.conf.RunOnReady != "" || path.conf.RunOnNotReady != "" {
|
||||
env = path.externalCmdEnv()
|
||||
desc := path.source.APISourceDescribe()
|
||||
env["MTX_QUERY"] = path.publisherQuery
|
||||
env["MTX_SOURCE_TYPE"] = desc.Type
|
||||
env["MTX_SOURCE_ID"] = desc.ID
|
||||
}
|
||||
|
||||
if path.conf.RunOnReady != "" {
|
||||
path.Log(logger.Info, "runOnReady command started")
|
||||
onReadyCmd = externalcmd.NewCmd(
|
||||
path.externalCmdPool,
|
||||
path.conf.RunOnReady,
|
||||
path.conf.RunOnReadyRestart,
|
||||
env,
|
||||
func(err error) {
|
||||
path.Log(logger.Info, "runOnReady command exited: %v", err)
|
||||
})
|
||||
}
|
||||
|
||||
return func() {
|
||||
if onReadyCmd != nil {
|
||||
onReadyCmd.Close()
|
||||
path.Log(logger.Info, "runOnReady command stopped")
|
||||
}
|
||||
|
||||
if path.conf.RunOnNotReady != "" {
|
||||
path.Log(logger.Info, "runOnNotReady command launched")
|
||||
externalcmd.NewCmd(
|
||||
path.externalCmdPool,
|
||||
path.conf.RunOnNotReady,
|
||||
false,
|
||||
env,
|
||||
nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func onReadHook(
|
||||
externalCmdPool *externalcmd.Pool,
|
||||
pathConf *conf.Path,
|
||||
path *path,
|
||||
reader defs.APIPathSourceOrReader,
|
||||
query string,
|
||||
l logger.Writer,
|
||||
) func() {
|
||||
var env externalcmd.Environment
|
||||
var onReadCmd *externalcmd.Cmd
|
||||
|
||||
if pathConf.RunOnRead != "" || pathConf.RunOnUnread != "" {
|
||||
env = path.externalCmdEnv()
|
||||
desc := reader
|
||||
env["MTX_QUERY"] = query
|
||||
env["MTX_READER_TYPE"] = desc.Type
|
||||
env["MTX_READER_ID"] = desc.ID
|
||||
}
|
||||
|
||||
if pathConf.RunOnRead != "" {
|
||||
l.Log(logger.Info, "runOnRead command started")
|
||||
onReadCmd = externalcmd.NewCmd(
|
||||
externalCmdPool,
|
||||
pathConf.RunOnRead,
|
||||
pathConf.RunOnReadRestart,
|
||||
env,
|
||||
func(err error) {
|
||||
l.Log(logger.Info, "runOnRead command exited: %v", err)
|
||||
})
|
||||
}
|
||||
|
||||
return func() {
|
||||
if onReadCmd != nil {
|
||||
onReadCmd.Close()
|
||||
l.Log(logger.Info, "runOnRead command stopped")
|
||||
}
|
||||
|
||||
if pathConf.RunOnUnread != "" {
|
||||
l.Log(logger.Info, "runOnUnread command launched")
|
||||
externalcmd.NewCmd(
|
||||
externalCmdPool,
|
||||
pathConf.RunOnUnread,
|
||||
false,
|
||||
env,
|
||||
nil)
|
||||
}
|
||||
}
|
||||
}
|
@ -304,18 +304,7 @@ func (pa *path) run() {
|
||||
}
|
||||
}
|
||||
|
||||
var onInitCmd *externalcmd.Cmd
|
||||
if pa.conf.RunOnInit != "" {
|
||||
pa.Log(logger.Info, "runOnInit command started")
|
||||
onInitCmd = externalcmd.NewCmd(
|
||||
pa.externalCmdPool,
|
||||
pa.conf.RunOnInit,
|
||||
pa.conf.RunOnInitRestart,
|
||||
pa.externalCmdEnv(),
|
||||
func(err error) {
|
||||
pa.Log(logger.Info, "runOnInit command exited: %v", err)
|
||||
})
|
||||
}
|
||||
onUnInitHook := onInitHook(pa)
|
||||
|
||||
err := pa.runInner()
|
||||
|
||||
@ -329,10 +318,7 @@ func (pa *path) run() {
|
||||
pa.onDemandPublisherReadyTimer.Stop()
|
||||
pa.onDemandPublisherCloseTimer.Stop()
|
||||
|
||||
if onInitCmd != nil {
|
||||
onInitCmd.Close()
|
||||
pa.Log(logger.Info, "runOnInit command stopped")
|
||||
}
|
||||
onUnInitHook()
|
||||
|
||||
for _, req := range pa.describeRequestsOnHold {
|
||||
req.res <- pathDescribeRes{err: fmt.Errorf("terminated")}
|
||||
@ -802,7 +788,7 @@ func (pa *path) onDemandStaticSourceStop(reason string) {
|
||||
}
|
||||
|
||||
func (pa *path) onDemandPublisherStart(query string) {
|
||||
pa.onUnDemandHook = publisherOnDemandHook(pa, query)
|
||||
pa.onUnDemandHook = onDemandHook(pa, query)
|
||||
|
||||
pa.onDemandPublisherReadyTimer.Stop()
|
||||
pa.onDemandPublisherReadyTimer = time.NewTimer(time.Duration(pa.conf.RunOnDemandStartTimeout))
|
||||
@ -847,7 +833,7 @@ func (pa *path) setReady(desc *description.Session, allocateEncoder bool) error
|
||||
|
||||
pa.readyTime = time.Now()
|
||||
|
||||
pa.onNotReadyHook = sourceOnReadyHook(pa)
|
||||
pa.onNotReadyHook = onReadyHook(pa)
|
||||
|
||||
pa.parent.pathReady(pa)
|
||||
|
||||
|
@ -84,6 +84,7 @@ func main() {
|
||||
|
||||
func TestPathRunOnDemand(t *testing.T) {
|
||||
onDemandFile := filepath.Join(os.TempDir(), "ondemand")
|
||||
onUnDemandFile := filepath.Join(os.TempDir(), "ondisconnect")
|
||||
|
||||
srcFile := filepath.Join(os.TempDir(), "ondemand.go")
|
||||
err := os.WriteFile(srcFile,
|
||||
@ -103,6 +104,7 @@ func TestPathRunOnDemand(t *testing.T) {
|
||||
for _, ca := range []string{"describe", "setup", "describe and setup"} {
|
||||
t.Run(ca, func(t *testing.T) {
|
||||
defer os.Remove(onDemandFile)
|
||||
defer os.Remove(onUnDemandFile)
|
||||
|
||||
p1, ok := newInstance(fmt.Sprintf("rtmp: no\n"+
|
||||
"hls: no\n"+
|
||||
@ -110,7 +112,8 @@ func TestPathRunOnDemand(t *testing.T) {
|
||||
"paths:\n"+
|
||||
" '~^(on)demand$':\n"+
|
||||
" runOnDemand: %s\n"+
|
||||
" runOnDemandCloseAfter: 1s\n", execFile))
|
||||
" runOnDemandCloseAfter: 1s\n"+
|
||||
" runOnUnDemand: touch %s\n", execFile, onUnDemandFile))
|
||||
require.Equal(t, true, ok)
|
||||
defer p1.Close()
|
||||
|
||||
@ -185,6 +188,9 @@ func TestPathRunOnDemand(t *testing.T) {
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
_, err := os.Stat(onUnDemandFile)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
||||
"github.com/bluenviron/mediamtx/internal/logger"
|
||||
)
|
||||
|
||||
// publisher is an entity that can publish a stream.
|
||||
type publisher interface {
|
||||
source
|
||||
close()
|
||||
}
|
||||
|
||||
func publisherOnDemandHook(path *path, query string) func(string) {
|
||||
var onDemandCmd *externalcmd.Cmd
|
||||
|
||||
if path.conf.RunOnDemand != "" {
|
||||
env := path.externalCmdEnv()
|
||||
env["MTX_QUERY"] = query
|
||||
|
||||
path.Log(logger.Info, "runOnDemand command started")
|
||||
|
||||
onDemandCmd = externalcmd.NewCmd(
|
||||
path.externalCmdPool,
|
||||
path.conf.RunOnDemand,
|
||||
path.conf.RunOnDemandRestart,
|
||||
env,
|
||||
func(err error) {
|
||||
path.Log(logger.Info, "runOnDemand command exited: %v", err)
|
||||
})
|
||||
}
|
||||
|
||||
return func(reason string) {
|
||||
if onDemandCmd != nil {
|
||||
onDemandCmd.Close()
|
||||
path.Log(logger.Info, "runOnDemand command stopped: %v", reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"github.com/bluenviron/mediamtx/internal/asyncwriter"
|
||||
"github.com/bluenviron/mediamtx/internal/conf"
|
||||
"github.com/bluenviron/mediamtx/internal/defs"
|
||||
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
||||
"github.com/bluenviron/mediamtx/internal/logger"
|
||||
"github.com/bluenviron/mediamtx/internal/stream"
|
||||
)
|
||||
|
||||
@ -18,52 +15,3 @@ type reader interface {
|
||||
func readerMediaInfo(r *asyncwriter.Writer, stream *stream.Stream) string {
|
||||
return mediaInfo(stream.MediasForReader(r))
|
||||
}
|
||||
|
||||
func readerOnReadHook(
|
||||
externalCmdPool *externalcmd.Pool,
|
||||
pathConf *conf.Path,
|
||||
path *path,
|
||||
reader defs.APIPathSourceOrReader,
|
||||
query string,
|
||||
l logger.Writer,
|
||||
) func() {
|
||||
var env externalcmd.Environment
|
||||
var onReadCmd *externalcmd.Cmd
|
||||
|
||||
if pathConf.RunOnRead != "" || pathConf.RunOnUnread != "" {
|
||||
env = path.externalCmdEnv()
|
||||
desc := reader
|
||||
env["MTX_QUERY"] = query
|
||||
env["MTX_READER_TYPE"] = desc.Type
|
||||
env["MTX_READER_ID"] = desc.ID
|
||||
}
|
||||
|
||||
if pathConf.RunOnRead != "" {
|
||||
l.Log(logger.Info, "runOnRead command started")
|
||||
onReadCmd = externalcmd.NewCmd(
|
||||
externalCmdPool,
|
||||
pathConf.RunOnRead,
|
||||
pathConf.RunOnReadRestart,
|
||||
env,
|
||||
func(err error) {
|
||||
l.Log(logger.Info, "runOnRead command exited: %v", err)
|
||||
})
|
||||
}
|
||||
|
||||
return func() {
|
||||
if onReadCmd != nil {
|
||||
onReadCmd.Close()
|
||||
l.Log(logger.Info, "runOnRead command stopped")
|
||||
}
|
||||
|
||||
if pathConf.RunOnUnread != "" {
|
||||
l.Log(logger.Info, "runOnUnread command launched")
|
||||
externalcmd.NewCmd(
|
||||
externalCmdPool,
|
||||
pathConf.RunOnUnread,
|
||||
false,
|
||||
env,
|
||||
nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ func (c *rtmpConn) run() { //nolint:dupl
|
||||
|
||||
desc := c.apiReaderDescribe()
|
||||
c.conn.open(desc)
|
||||
defer c.conn.close(desc)
|
||||
defer c.conn.close()
|
||||
|
||||
err := c.runInner()
|
||||
|
||||
@ -256,7 +256,7 @@ func (c *rtmpConn) runRead(conn *rtmp.Conn, u *url.URL) error {
|
||||
|
||||
pathConf := res.path.safeConf()
|
||||
|
||||
onUnreadHook := readerOnReadHook(
|
||||
onUnreadHook := onReadHook(
|
||||
c.externalCmdPool,
|
||||
pathConf,
|
||||
res.path,
|
||||
|
@ -114,15 +114,7 @@ func (c *rtspConn) ip() net.IP {
|
||||
func (c *rtspConn) onClose(err error) {
|
||||
c.Log(logger.Info, "closed: %v", err)
|
||||
|
||||
c.conn.close(defs.APIPathSourceOrReader{
|
||||
Type: func() string {
|
||||
if c.isTLS {
|
||||
return "rtspsConn"
|
||||
}
|
||||
return "rtspConn"
|
||||
}(),
|
||||
ID: c.uuid.String(),
|
||||
})
|
||||
c.conn.close()
|
||||
}
|
||||
|
||||
// onRequest is called by rtspServer.
|
||||
|
@ -292,7 +292,7 @@ func (s *rtspSession) onPlay(_ *gortsplib.ServerHandlerOnPlayCtx) (*base.Respons
|
||||
|
||||
pathConf := s.path.safeConf()
|
||||
|
||||
s.onUnreadHook = readerOnReadHook(
|
||||
s.onUnreadHook = onReadHook(
|
||||
s.externalCmdPool,
|
||||
pathConf,
|
||||
s.path,
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/defs"
|
||||
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
||||
"github.com/bluenviron/mediamtx/internal/logger"
|
||||
)
|
||||
|
||||
@ -48,45 +47,3 @@ func mediaInfo(medias []*description.Media) string {
|
||||
}(),
|
||||
strings.Join(mediasDescription(medias), ", "))
|
||||
}
|
||||
|
||||
func sourceOnReadyHook(path *path) func() {
|
||||
var env externalcmd.Environment
|
||||
var onReadyCmd *externalcmd.Cmd
|
||||
|
||||
if path.conf.RunOnReady != "" {
|
||||
env = path.externalCmdEnv()
|
||||
desc := path.source.APISourceDescribe()
|
||||
env["MTX_QUERY"] = path.publisherQuery
|
||||
env["MTX_SOURCE_TYPE"] = desc.Type
|
||||
env["MTX_SOURCE_ID"] = desc.ID
|
||||
}
|
||||
|
||||
if path.conf.RunOnReady != "" {
|
||||
path.Log(logger.Info, "runOnReady command started")
|
||||
onReadyCmd = externalcmd.NewCmd(
|
||||
path.externalCmdPool,
|
||||
path.conf.RunOnReady,
|
||||
path.conf.RunOnReadyRestart,
|
||||
env,
|
||||
func(err error) {
|
||||
path.Log(logger.Info, "runOnReady command exited: %v", err)
|
||||
})
|
||||
}
|
||||
|
||||
return func() {
|
||||
if onReadyCmd != nil {
|
||||
onReadyCmd.Close()
|
||||
path.Log(logger.Info, "runOnReady command stopped")
|
||||
}
|
||||
|
||||
if path.conf.RunOnNotReady != "" {
|
||||
path.Log(logger.Info, "runOnNotReady command launched")
|
||||
externalcmd.NewCmd(
|
||||
path.externalCmdPool,
|
||||
path.conf.RunOnNotReady,
|
||||
false,
|
||||
env,
|
||||
nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ func (c *srtConn) run() { //nolint:dupl
|
||||
|
||||
desc := c.apiReaderDescribe()
|
||||
c.conn.open(desc)
|
||||
defer c.conn.close(desc)
|
||||
defer c.conn.close()
|
||||
|
||||
err := c.runInner()
|
||||
|
||||
@ -363,7 +363,7 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
|
||||
|
||||
pathConf := res.path.safeConf()
|
||||
|
||||
onUnreadHook := readerOnReadHook(
|
||||
onUnreadHook := onReadHook(
|
||||
c.externalCmdPool,
|
||||
pathConf,
|
||||
res.path,
|
||||
|
@ -612,7 +612,7 @@ func (s *webRTCSession) runRead() (int, error) {
|
||||
|
||||
pathConf := res.path.safeConf()
|
||||
|
||||
onUnreadHook := readerOnReadHook(
|
||||
onUnreadHook := onReadHook(
|
||||
s.externalCmdPool,
|
||||
pathConf,
|
||||
res.path,
|
||||
|
@ -471,7 +471,7 @@ pathDefaults:
|
||||
# Command to run when this path is requested by a reader
|
||||
# and no one is publishing to this path yet.
|
||||
# This can be used to publish a stream on demand.
|
||||
# This is terminated with SIGINT when the path is not requested anymore.
|
||||
# This is terminated with SIGINT when there are no readers anymore.
|
||||
# The following environment variables are available:
|
||||
# * MTX_PATH: path name
|
||||
# * MTX_QUERY: query parameters (passed by first reader)
|
||||
@ -487,6 +487,9 @@ pathDefaults:
|
||||
# The command will be closed when there are no
|
||||
# readers connected and this amount of time has passed.
|
||||
runOnDemandCloseAfter: 10s
|
||||
# Command to run when there are no readers anymore.
|
||||
# Environment variables are the same of runOnDemand.
|
||||
runOnUnDemand:
|
||||
|
||||
# Command to run when the stream is ready to be read, whenever it is
|
||||
# published by a client or pulled from a server / camera.
|
||||
|
Loading…
Reference in New Issue
Block a user