Browse Source

support relative fallback paths (#201)

pull/235/head v0.14.1
aler9 6 years ago
parent
commit
22ab4d489e
  1. 4
      README.md
  2. 20
      internal/conf/path.go
  3. 15
      internal/path/path.go
  4. 70
      main_test.go
  5. 9
      rtsp-simple-server.yml

4
README.md

@ -414,12 +414,12 @@ paths:
### Fallback stream ### Fallback stream
If no one is publishing to the server, readers can be redirected to a fallback URL that is serving a fallback stream: If no one is publishing to the server, readers can be redirected to a fallback path or URL that is serving a fallback stream:
```yml ```yml
paths: paths:
withfallback: withfallback:
fallback: rtsp://otherurl/otherpath fallback: /otherpath
``` ```
### Start on boot with systemd ### Start on boot with systemd

20
internal/conf/path.go

@ -198,8 +198,8 @@ func (pconf *PathConf) fillAndCheck(name string) error {
} }
if pconf.SourceOnDemand { if pconf.SourceOnDemand {
if pconf.Source != "record" { if pconf.Source == "record" {
return fmt.Errorf("'sourceOnDemand' is useless when source is not 'record', since the stream is not provided by a publisher, but by a fixed source") return fmt.Errorf("'sourceOnDemand' is useless when source is 'record'")
} }
} }
@ -212,9 +212,17 @@ func (pconf *PathConf) fillAndCheck(name string) error {
} }
if pconf.Fallback != "" { if pconf.Fallback != "" {
_, err := base.ParseURL(pconf.Fallback) if strings.HasPrefix(pconf.Fallback, "/") {
if err != nil { err := CheckPathName(pconf.Fallback[1:])
return fmt.Errorf("'%s' is not a valid RTSP url", pconf.Fallback) 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)
}
} }
} }
@ -223,7 +231,7 @@ func (pconf *PathConf) fillAndCheck(name string) error {
} }
if pconf.PublishUser != "" { if pconf.PublishUser != "" {
if pconf.Source != "record" { if pconf.Source != "record" {
return fmt.Errorf("'publishUser' is useless when source is not 'record', since the stream is not provided by a publisher, but by a fixed source") return fmt.Errorf("'publishUser' is useless when source is not 'record'")
} }
if !strings.HasPrefix(pconf.PublishUser, "sha256:") && !reUserPass.MatchString(pconf.PublishUser) { if !strings.HasPrefix(pconf.PublishUser, "sha256:") && !reUserPass.MatchString(pconf.PublishUser) {

15
internal/path/path.go

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/aler9/gortsplib" "github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/base"
"github.com/aler9/rtsp-simple-server/internal/client" "github.com/aler9/rtsp-simple-server/internal/client"
"github.com/aler9/rtsp-simple-server/internal/clientrtsp" "github.com/aler9/rtsp-simple-server/internal/clientrtsp"
@ -598,7 +599,19 @@ func (pa *Path) onClientDescribe(req client.DescribeReq) {
case sourceStateNotReady: case sourceStateNotReady:
if pa.conf.Fallback != "" { if pa.conf.Fallback != "" {
req.Res <- client.DescribeRes{nil, pa.conf.Fallback, nil} //nolint:govet fallbackURL := func() string {
if strings.HasPrefix(pa.conf.Fallback, "/") {
ur := base.URL{
Scheme: req.Req.URL.Scheme,
User: req.Req.URL.User,
Host: req.Req.URL.Host,
Path: pa.conf.Fallback,
}
return ur.String()
}
return pa.conf.Fallback
}()
req.Res <- client.DescribeRes{nil, fallbackURL, nil} //nolint:govet
return return
} }

70
main_test.go

@ -851,37 +851,51 @@ func TestRedirect(t *testing.T) {
} }
func TestFallback(t *testing.T) { func TestFallback(t *testing.T) {
p1, ok := testProgram("paths:\n" + for _, ca := range []string{
" path1:\n" + "absolute",
" fallback: rtsp://" + ownDockerIP + ":8554/path2\n" + "relative",
" path2:\n") } {
require.Equal(t, true, ok) t.Run(ca, func(t *testing.T) {
defer p1.close() val := func() string {
if ca == "absolute" {
return "rtsp://" + ownDockerIP + ":8554/path2"
}
return "/path2"
}()
cnt1, err := newContainer("ffmpeg", "source", []string{ p1, ok := testProgram("paths:\n" +
"-re", " path1:\n" +
"-stream_loop", "-1", " fallback: " + val + "\n" +
"-i", "emptyvideo.ts", " path2:\n")
"-c", "copy", require.Equal(t, true, ok)
"-f", "rtsp", defer p1.close()
"-rtsp_transport", "udp",
"rtsp://" + ownDockerIP + ":8554/path2",
})
require.NoError(t, err)
defer cnt1.close()
time.Sleep(1 * time.Second) cnt1, err := newContainer("ffmpeg", "source", []string{
"-re",
"-stream_loop", "-1",
"-i", "emptyvideo.ts",
"-c", "copy",
"-f", "rtsp",
"-rtsp_transport", "udp",
"rtsp://" + ownDockerIP + ":8554/path2",
})
require.NoError(t, err)
defer cnt1.close()
cnt2, err := newContainer("ffmpeg", "dest", []string{ time.Sleep(1 * time.Second)
"-rtsp_transport", "udp",
"-i", "rtsp://" + ownDockerIP + ":8554/path1", cnt2, err := newContainer("ffmpeg", "dest", []string{
"-vframes", "1", "-rtsp_transport", "udp",
"-f", "image2", "-i", "rtsp://" + ownDockerIP + ":8554/path1",
"-y", "/dev/null", "-vframes", "1",
}) "-f", "image2",
require.NoError(t, err) "-y", "/dev/null",
defer cnt2.close() })
require.Equal(t, 0, cnt2.wait()) require.NoError(t, err)
defer cnt2.close()
require.Equal(t, 0, cnt2.wait())
})
}
} }
func TestRTMP(t *testing.T) { func TestRTMP(t *testing.T) {

9
rtsp-simple-server.yml

@ -86,12 +86,12 @@ paths:
# * redirect -> the stream is provided by another path or server # * redirect -> the stream is provided by another path or server
source: record source: record
# if the source is an RTSP url, this is the protocol that will be used to # if the source is an RTSP URL, this is the protocol that will be used to
# pull the stream. available options are "automatic", "udp", "tcp". # pull the stream. available options are "automatic", "udp", "tcp".
# the tcp protocol can help to overcome the error "no UDP packets received recently". # the tcp protocol can help to overcome the error "no UDP packets received recently".
sourceProtocol: automatic sourceProtocol: automatic
# if the source is an RTSP or RTMP url, it will be pulled only when at least # if the source is an RTSP or RTMP URL, it will be pulled only when at least
# one reader is connected, saving bandwidth. # one reader is connected, saving bandwidth.
sourceOnDemand: no sourceOnDemand: no
# if sourceOnDemand is "yes", readers will be put on hold until the source is # if sourceOnDemand is "yes", readers will be put on hold until the source is
@ -101,11 +101,12 @@ paths:
# readers connected and this amount of time has passed. # readers connected and this amount of time has passed.
sourceOnDemandCloseAfter: 10s sourceOnDemandCloseAfter: 10s
# if the source is "redirect", this is the RTSP url which clients will be # if the source is "redirect", this is the RTSP URL which clients will be
# redirected to. # redirected to.
sourceRedirect: sourceRedirect:
# fallback url to redirect clients to when nobody is publishing to this path. # fallback stream to redirect clients to when nobody is publishing to this path.
# this can be a relative path (i.e. /otherstream) or an absolute RTSP URL.
fallback: fallback:
# username required to publish. # username required to publish.

Loading…
Cancel
Save