Skip to content

Commit 22ab4d4

Browse files
committed
support relative fallback paths (#201)
1 parent 86c718a commit 22ab4d4

File tree

5 files changed

+77
-41
lines changed

5 files changed

+77
-41
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,12 +414,12 @@ paths:
414414

415415
### Fallback stream
416416

417-
If no one is publishing to the server, readers can be redirected to a fallback URL that is serving a fallback stream:
417+
If no one is publishing to the server, readers can be redirected to a fallback path or URL that is serving a fallback stream:
418418

419419
```yml
420420
paths:
421421
withfallback:
422-
fallback: rtsp://otherurl/otherpath
422+
fallback: /otherpath
423423
```
424424

425425
### Start on boot with systemd

internal/conf/path.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ func (pconf *PathConf) fillAndCheck(name string) error {
198198
}
199199

200200
if pconf.SourceOnDemand {
201-
if pconf.Source != "record" {
202-
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")
201+
if pconf.Source == "record" {
202+
return fmt.Errorf("'sourceOnDemand' is useless when source is 'record'")
203203
}
204204
}
205205

@@ -212,9 +212,17 @@ func (pconf *PathConf) fillAndCheck(name string) error {
212212
}
213213

214214
if pconf.Fallback != "" {
215-
_, err := base.ParseURL(pconf.Fallback)
216-
if err != nil {
217-
return fmt.Errorf("'%s' is not a valid RTSP url", pconf.Fallback)
215+
if strings.HasPrefix(pconf.Fallback, "/") {
216+
err := CheckPathName(pconf.Fallback[1:])
217+
if err != nil {
218+
return fmt.Errorf("'%s': %s", pconf.Fallback, err)
219+
}
220+
221+
} else {
222+
_, err := base.ParseURL(pconf.Fallback)
223+
if err != nil {
224+
return fmt.Errorf("'%s' is not a valid RTSP url", pconf.Fallback)
225+
}
218226
}
219227
}
220228

@@ -223,7 +231,7 @@ func (pconf *PathConf) fillAndCheck(name string) error {
223231
}
224232
if pconf.PublishUser != "" {
225233
if pconf.Source != "record" {
226-
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")
234+
return fmt.Errorf("'publishUser' is useless when source is not 'record'")
227235
}
228236

229237
if !strings.HasPrefix(pconf.PublishUser, "sha256:") && !reUserPass.MatchString(pconf.PublishUser) {

internal/path/path.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
"github.com/aler9/gortsplib"
12+
"github.com/aler9/gortsplib/pkg/base"
1213

1314
"github.com/aler9/rtsp-simple-server/internal/client"
1415
"github.com/aler9/rtsp-simple-server/internal/clientrtsp"
@@ -598,7 +599,19 @@ func (pa *Path) onClientDescribe(req client.DescribeReq) {
598599

599600
case sourceStateNotReady:
600601
if pa.conf.Fallback != "" {
601-
req.Res <- client.DescribeRes{nil, pa.conf.Fallback, nil} //nolint:govet
602+
fallbackURL := func() string {
603+
if strings.HasPrefix(pa.conf.Fallback, "/") {
604+
ur := base.URL{
605+
Scheme: req.Req.URL.Scheme,
606+
User: req.Req.URL.User,
607+
Host: req.Req.URL.Host,
608+
Path: pa.conf.Fallback,
609+
}
610+
return ur.String()
611+
}
612+
return pa.conf.Fallback
613+
}()
614+
req.Res <- client.DescribeRes{nil, fallbackURL, nil} //nolint:govet
602615
return
603616
}
604617

main_test.go

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -851,37 +851,51 @@ func TestRedirect(t *testing.T) {
851851
}
852852

853853
func TestFallback(t *testing.T) {
854-
p1, ok := testProgram("paths:\n" +
855-
" path1:\n" +
856-
" fallback: rtsp://" + ownDockerIP + ":8554/path2\n" +
857-
" path2:\n")
858-
require.Equal(t, true, ok)
859-
defer p1.close()
854+
for _, ca := range []string{
855+
"absolute",
856+
"relative",
857+
} {
858+
t.Run(ca, func(t *testing.T) {
859+
val := func() string {
860+
if ca == "absolute" {
861+
return "rtsp://" + ownDockerIP + ":8554/path2"
862+
}
863+
return "/path2"
864+
}()
860865

861-
cnt1, err := newContainer("ffmpeg", "source", []string{
862-
"-re",
863-
"-stream_loop", "-1",
864-
"-i", "emptyvideo.ts",
865-
"-c", "copy",
866-
"-f", "rtsp",
867-
"-rtsp_transport", "udp",
868-
"rtsp://" + ownDockerIP + ":8554/path2",
869-
})
870-
require.NoError(t, err)
871-
defer cnt1.close()
866+
p1, ok := testProgram("paths:\n" +
867+
" path1:\n" +
868+
" fallback: " + val + "\n" +
869+
" path2:\n")
870+
require.Equal(t, true, ok)
871+
defer p1.close()
872872

873-
time.Sleep(1 * time.Second)
873+
cnt1, err := newContainer("ffmpeg", "source", []string{
874+
"-re",
875+
"-stream_loop", "-1",
876+
"-i", "emptyvideo.ts",
877+
"-c", "copy",
878+
"-f", "rtsp",
879+
"-rtsp_transport", "udp",
880+
"rtsp://" + ownDockerIP + ":8554/path2",
881+
})
882+
require.NoError(t, err)
883+
defer cnt1.close()
874884

875-
cnt2, err := newContainer("ffmpeg", "dest", []string{
876-
"-rtsp_transport", "udp",
877-
"-i", "rtsp://" + ownDockerIP + ":8554/path1",
878-
"-vframes", "1",
879-
"-f", "image2",
880-
"-y", "/dev/null",
881-
})
882-
require.NoError(t, err)
883-
defer cnt2.close()
884-
require.Equal(t, 0, cnt2.wait())
885+
time.Sleep(1 * time.Second)
886+
887+
cnt2, err := newContainer("ffmpeg", "dest", []string{
888+
"-rtsp_transport", "udp",
889+
"-i", "rtsp://" + ownDockerIP + ":8554/path1",
890+
"-vframes", "1",
891+
"-f", "image2",
892+
"-y", "/dev/null",
893+
})
894+
require.NoError(t, err)
895+
defer cnt2.close()
896+
require.Equal(t, 0, cnt2.wait())
897+
})
898+
}
885899
}
886900

887901
func TestRTMP(t *testing.T) {

rtsp-simple-server.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ paths:
8686
# * redirect -> the stream is provided by another path or server
8787
source: record
8888

89-
# if the source is an RTSP url, this is the protocol that will be used to
89+
# if the source is an RTSP URL, this is the protocol that will be used to
9090
# pull the stream. available options are "automatic", "udp", "tcp".
9191
# the tcp protocol can help to overcome the error "no UDP packets received recently".
9292
sourceProtocol: automatic
9393

94-
# if the source is an RTSP or RTMP url, it will be pulled only when at least
94+
# if the source is an RTSP or RTMP URL, it will be pulled only when at least
9595
# one reader is connected, saving bandwidth.
9696
sourceOnDemand: no
9797
# if sourceOnDemand is "yes", readers will be put on hold until the source is
@@ -101,11 +101,12 @@ paths:
101101
# readers connected and this amount of time has passed.
102102
sourceOnDemandCloseAfter: 10s
103103

104-
# if the source is "redirect", this is the RTSP url which clients will be
104+
# if the source is "redirect", this is the RTSP URL which clients will be
105105
# redirected to.
106106
sourceRedirect:
107107

108-
# fallback url to redirect clients to when nobody is publishing to this path.
108+
# fallback stream to redirect clients to when nobody is publishing to this path.
109+
# this can be a relative path (i.e. /otherstream) or an absolute RTSP URL.
109110
fallback:
110111

111112
# username required to publish.

0 commit comments

Comments
 (0)