forked from External/mediamtx
parent
4fa6c16c85
commit
c3d9be322d
5 changed files with 74 additions and 67 deletions
|
|
@ -210,6 +210,8 @@ components:
|
||||||
type: integer
|
type: integer
|
||||||
srtReadPassphrase:
|
srtReadPassphrase:
|
||||||
type: string
|
type: string
|
||||||
|
fallback:
|
||||||
|
type: string
|
||||||
|
|
||||||
# Record
|
# Record
|
||||||
record:
|
record:
|
||||||
|
|
@ -246,8 +248,6 @@ components:
|
||||||
# Publisher source
|
# Publisher source
|
||||||
overridePublisher:
|
overridePublisher:
|
||||||
type: boolean
|
type: boolean
|
||||||
fallback:
|
|
||||||
type: string
|
|
||||||
srtPublishPassphrase:
|
srtPublishPassphrase:
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ type Path struct {
|
||||||
SourceOnDemandCloseAfter StringDuration `json:"sourceOnDemandCloseAfter"`
|
SourceOnDemandCloseAfter StringDuration `json:"sourceOnDemandCloseAfter"`
|
||||||
MaxReaders int `json:"maxReaders"`
|
MaxReaders int `json:"maxReaders"`
|
||||||
SRTReadPassphrase string `json:"srtReadPassphrase"`
|
SRTReadPassphrase string `json:"srtReadPassphrase"`
|
||||||
|
Fallback string `json:"fallback"`
|
||||||
|
|
||||||
// Record
|
// Record
|
||||||
Record bool `json:"record"`
|
Record bool `json:"record"`
|
||||||
|
|
@ -80,7 +81,6 @@ type Path struct {
|
||||||
// Publisher source
|
// Publisher source
|
||||||
OverridePublisher bool `json:"overridePublisher"`
|
OverridePublisher bool `json:"overridePublisher"`
|
||||||
DisablePublisherOverride *bool `json:"disablePublisherOverride,omitempty"` // deprecated
|
DisablePublisherOverride *bool `json:"disablePublisherOverride,omitempty"` // deprecated
|
||||||
Fallback string `json:"fallback"`
|
|
||||||
SRTPublishPassphrase string `json:"srtPublishPassphrase"`
|
SRTPublishPassphrase string `json:"srtPublishPassphrase"`
|
||||||
|
|
||||||
// RTSP source
|
// RTSP source
|
||||||
|
|
@ -346,6 +346,19 @@ func (pconf *Path) check(conf *Conf, name string) error {
|
||||||
return fmt.Errorf("invalid 'readRTPassphrase': %v", err)
|
return fmt.Errorf("invalid 'readRTPassphrase': %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if pconf.Fallback != "" {
|
||||||
|
if strings.HasPrefix(pconf.Fallback, "/") {
|
||||||
|
err := IsValidPathName(pconf.Fallback[1:])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("'%s': %s", pconf.Fallback, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err := base.ParseURL(pconf.Fallback)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("'%s' is not a valid RTSP URL", pconf.Fallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
|
|
||||||
|
|
@ -387,23 +400,6 @@ func (pconf *Path) check(conf *Conf, name string) error {
|
||||||
if pconf.DisablePublisherOverride != nil {
|
if pconf.DisablePublisherOverride != nil {
|
||||||
pconf.OverridePublisher = !*pconf.DisablePublisherOverride
|
pconf.OverridePublisher = !*pconf.DisablePublisherOverride
|
||||||
}
|
}
|
||||||
if pconf.Fallback != "" {
|
|
||||||
if pconf.Source != "publisher" {
|
|
||||||
return fmt.Errorf("'fallback' can only be used when source is 'publisher'")
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(pconf.Fallback, "/") {
|
|
||||||
err := IsValidPathName(pconf.Fallback[1:])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("'%s': %s", pconf.Fallback, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, err := base.ParseURL(pconf.Fallback)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("'%s' is not a valid RTSP URL", pconf.Fallback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pconf.SRTPublishPassphrase != "" {
|
if pconf.SRTPublishPassphrase != "" {
|
||||||
if pconf.Source != "publisher" {
|
if pconf.Source != "publisher" {
|
||||||
return fmt.Errorf("'srtPublishPassphase' can only be used when source is 'publisher'")
|
return fmt.Errorf("'srtPublishPassphase' can only be used when source is 'publisher'")
|
||||||
|
|
|
||||||
|
|
@ -515,3 +515,58 @@ func TestPathRecord(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 2, len(files))
|
require.Equal(t, 2, len(files))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPathFallback(t *testing.T) {
|
||||||
|
for _, ca := range []string{
|
||||||
|
"absolute",
|
||||||
|
"relative",
|
||||||
|
"source",
|
||||||
|
} {
|
||||||
|
t.Run(ca, func(t *testing.T) {
|
||||||
|
var conf string
|
||||||
|
|
||||||
|
switch ca {
|
||||||
|
case "absolute":
|
||||||
|
conf = "paths:\n" +
|
||||||
|
" path1:\n" +
|
||||||
|
" fallback: rtsp://localhost:8554/path2\n" +
|
||||||
|
" path2:\n"
|
||||||
|
|
||||||
|
case "relative":
|
||||||
|
conf = "paths:\n" +
|
||||||
|
" path1:\n" +
|
||||||
|
" fallback: /path2\n" +
|
||||||
|
" path2:\n"
|
||||||
|
|
||||||
|
case "source":
|
||||||
|
conf = "paths:\n" +
|
||||||
|
" path1:\n" +
|
||||||
|
" fallback: /path2\n" +
|
||||||
|
" source: rtsp://localhost:3333/nonexistent\n" +
|
||||||
|
" path2:\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
p1, ok := newInstance(conf)
|
||||||
|
require.Equal(t, true, ok)
|
||||||
|
defer p1.Close()
|
||||||
|
|
||||||
|
source := gortsplib.Client{}
|
||||||
|
err := source.StartRecording("rtsp://localhost:8554/path2",
|
||||||
|
&description.Session{Medias: []*description.Media{testMediaH264}})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer source.Close()
|
||||||
|
|
||||||
|
u, err := base.ParseURL("rtsp://localhost:8554/path1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dest := gortsplib.Client{}
|
||||||
|
err = dest.Start(u.Scheme, u.Host)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer dest.Close()
|
||||||
|
|
||||||
|
desc, _, err := dest.Describe(u)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(desc.Medias))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -348,47 +348,3 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRTSPServerFallback(t *testing.T) {
|
|
||||||
for _, ca := range []string{
|
|
||||||
"absolute",
|
|
||||||
"relative",
|
|
||||||
} {
|
|
||||||
t.Run(ca, func(t *testing.T) {
|
|
||||||
val := func() string {
|
|
||||||
if ca == "absolute" {
|
|
||||||
return "rtsp://localhost:8554/path2"
|
|
||||||
}
|
|
||||||
return "/path2"
|
|
||||||
}()
|
|
||||||
|
|
||||||
p1, ok := newInstance("rtmp: no\n" +
|
|
||||||
"hls: no\n" +
|
|
||||||
"webrtc: no\n" +
|
|
||||||
"paths:\n" +
|
|
||||||
" path1:\n" +
|
|
||||||
" fallback: " + val + "\n" +
|
|
||||||
" path2:\n")
|
|
||||||
require.Equal(t, true, ok)
|
|
||||||
defer p1.Close()
|
|
||||||
|
|
||||||
source := gortsplib.Client{}
|
|
||||||
err := source.StartRecording("rtsp://localhost:8554/path2",
|
|
||||||
&description.Session{Medias: []*description.Media{testMediaH264}})
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer source.Close()
|
|
||||||
|
|
||||||
u, err := base.ParseURL("rtsp://localhost:8554/path1")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
dest := gortsplib.Client{}
|
|
||||||
err = dest.Start(u.Scheme, u.Host)
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer dest.Close()
|
|
||||||
|
|
||||||
desc, _, err := dest.Describe(u)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 1, len(desc.Medias))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,9 @@ pathDefaults:
|
||||||
maxReaders: 0
|
maxReaders: 0
|
||||||
# SRT encryption passphrase require to read from this path
|
# SRT encryption passphrase require to read from this path
|
||||||
srtReadPassphrase:
|
srtReadPassphrase:
|
||||||
|
# If the stream is not available, redirect readers to this path.
|
||||||
|
# It can be can be a relative path (i.e. /otherstream) or an absolute RTSP URL.
|
||||||
|
fallback:
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# Default path settings -> Recording
|
# Default path settings -> Recording
|
||||||
|
|
@ -335,9 +338,6 @@ pathDefaults:
|
||||||
|
|
||||||
# Allow another client to disconnect the current publisher and publish in its place.
|
# Allow another client to disconnect the current publisher and publish in its place.
|
||||||
overridePublisher: yes
|
overridePublisher: yes
|
||||||
# If no one is publishing, redirect readers to this path.
|
|
||||||
# It can be can be a relative path (i.e. /otherstream) or an absolute RTSP URL.
|
|
||||||
fallback:
|
|
||||||
# SRT encryption passphrase required to publish to this path
|
# SRT encryption passphrase required to publish to this path
|
||||||
srtPublishPassphrase:
|
srtPublishPassphrase:
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue