rename sourceProtocol into rtspTransport, sourceAnyPortEnable into rtspAnyPort (#2644)

This commit is contained in:
Alessandro Ros 2023-11-04 11:12:22 +01:00 committed by GitHub
parent faf7218fda
commit 1d1d64cb89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 120 deletions

View File

@ -1546,7 +1546,7 @@ In some scenarios, when publishing or reading from the server with RTSP, frames
paths:
test:
source: rtsp://..
sourceProtocol: tcp
rtspTransport: tcp
```
* The stream throughput is too big to be handled by the network between server and readers. Upgrade the network or decrease the stream bitrate by re-encoding it.

View File

@ -252,9 +252,9 @@ components:
type: string
# RTSP source
sourceProtocol:
rtspTransport:
type: string
sourceAnyPortEnable:
rtspAnyPort:
type: boolean
rtspRangeType:
type: string

View File

@ -40,7 +40,7 @@ CONF="${CONF}paths:\n"
for i in $(seq 1 $PROXY_COUNT); do
CONF="${CONF} proxy$i:\n"
CONF="${CONF} source: rtsp://localhost:8555/source\n"
CONF="${CONF} sourceProtocol: $PROXY_PROTOCOL\n"
CONF="${CONF} rtspTransport: $PROXY_PROTOCOL\n"
done
echo -e "$CONF" > /proxy.conf

View File

@ -84,8 +84,10 @@ type Path struct {
SRTPublishPassphrase string `json:"srtPublishPassphrase"`
// RTSP source
SourceProtocol SourceProtocol `json:"sourceProtocol"`
SourceAnyPortEnable bool `json:"sourceAnyPortEnable"`
RTSPTransport RTSPTransport `json:"rtspTransport"`
RTSPAnyPort bool `json:"rtspAnyPort"`
SourceProtocol *RTSPTransport `json:"sourceProtocol,omitempty"` // deprecated
SourceAnyPortEnable *bool `json:"sourceAnyPortEnable,omitempty"` // deprecated
RTSPRangeType RTSPRangeType `json:"rtspRangeType"`
RTSPRangeStart string `json:"rtspRangeStart"`
@ -322,14 +324,6 @@ func (pconf *Path) check(conf *Conf, name string) error {
}
case pconf.Source == "redirect":
if pconf.SourceRedirect == "" {
return fmt.Errorf("source redirect must be filled")
}
_, err := url.Parse(pconf.SourceRedirect)
if err != nil {
return fmt.Errorf("'%s' is not a valid RTSP URL", pconf.SourceRedirect)
}
case pconf.Source == "rpiCamera":
if pconf.Regexp != nil {
@ -337,60 +331,9 @@ func (pconf *Path) check(conf *Conf, name string) error {
"a path with a regular expression (or path 'all') cannot have 'rpiCamera' as source. use another path")
}
for otherName, otherPath := range conf.Paths {
if otherPath != pconf && otherPath != nil &&
otherPath.Source == "rpiCamera" && otherPath.RPICameraCamID == pconf.RPICameraCamID {
return fmt.Errorf("'rpiCamera' with same camera ID %d is used as source in two paths, '%s' and '%s'",
pconf.RPICameraCamID, name, otherName)
}
}
switch pconf.RPICameraExposure {
case "normal", "short", "long", "custom":
default:
return fmt.Errorf("invalid 'rpiCameraExposure' value")
}
switch pconf.RPICameraAWB {
case "auto", "incandescent", "tungsten", "fluorescent", "indoor", "daylight", "cloudy", "custom":
default:
return fmt.Errorf("invalid 'rpiCameraAWB' value")
}
switch pconf.RPICameraDenoise {
case "off", "cdn_off", "cdn_fast", "cdn_hq":
default:
return fmt.Errorf("invalid 'rpiCameraDenoise' value")
}
switch pconf.RPICameraMetering {
case "centre", "spot", "matrix", "custom":
default:
return fmt.Errorf("invalid 'rpiCameraMetering' value")
}
switch pconf.RPICameraAfMode {
case "auto", "manual", "continuous":
default:
return fmt.Errorf("invalid 'rpiCameraAfMode' value")
}
switch pconf.RPICameraAfRange {
case "normal", "macro", "full":
default:
return fmt.Errorf("invalid 'rpiCameraAfRange' value")
}
switch pconf.RPICameraAfSpeed {
case "normal", "fast":
default:
return fmt.Errorf("invalid 'rpiCameraAfSpeed' value")
}
default:
return fmt.Errorf("invalid source: '%s'", pconf.Source)
}
if pconf.SourceOnDemand {
if pconf.Source == "publisher" {
return fmt.Errorf("'sourceOnDemand' is useless when source is 'publisher'")
@ -403,39 +346,6 @@ func (pconf *Path) check(conf *Conf, name string) error {
}
}
// Publisher
if pconf.DisablePublisherOverride != nil {
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 := url.Parse(pconf.Fallback)
if err != nil {
return fmt.Errorf("'%s' is not a valid RTSP URL", pconf.Fallback)
}
}
}
if pconf.SRTPublishPassphrase != "" {
if pconf.Source != "publisher" {
return fmt.Errorf("'srtPublishPassphase' can only be used when source is 'publisher'")
}
err := srtCheckPassphrase(pconf.SRTPublishPassphrase)
if err != nil {
return fmt.Errorf("invalid 'srtPublishPassphrase': %v", err)
}
}
// Authentication
if (pconf.PublishUser != "" && pconf.PublishPass == "") ||
@ -471,6 +381,108 @@ func (pconf *Path) check(conf *Conf, name string) error {
}
}
// Publisher source
if pconf.DisablePublisherOverride != nil {
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 := url.Parse(pconf.Fallback)
if err != nil {
return fmt.Errorf("'%s' is not a valid RTSP URL", pconf.Fallback)
}
}
}
if pconf.SRTPublishPassphrase != "" {
if pconf.Source != "publisher" {
return fmt.Errorf("'srtPublishPassphase' can only be used when source is 'publisher'")
}
err := srtCheckPassphrase(pconf.SRTPublishPassphrase)
if err != nil {
return fmt.Errorf("invalid 'srtPublishPassphrase': %v", err)
}
}
// RTSP source
if pconf.SourceProtocol != nil {
pconf.RTSPTransport = *pconf.SourceProtocol
}
if pconf.SourceAnyPortEnable != nil {
pconf.RTSPAnyPort = *pconf.SourceAnyPortEnable
}
// Redirect source
if pconf.Source == "redirect" {
if pconf.SourceRedirect == "" {
return fmt.Errorf("source redirect must be filled")
}
_, err := url.Parse(pconf.SourceRedirect)
if err != nil {
return fmt.Errorf("'%s' is not a valid RTSP URL", pconf.SourceRedirect)
}
}
// Raspberry Pi Camera source
if pconf.Source == "rpiCamera" {
for otherName, otherPath := range conf.Paths {
if otherPath != pconf && otherPath != nil &&
otherPath.Source == "rpiCamera" && otherPath.RPICameraCamID == pconf.RPICameraCamID {
return fmt.Errorf("'rpiCamera' with same camera ID %d is used as source in two paths, '%s' and '%s'",
pconf.RPICameraCamID, name, otherName)
}
}
}
switch pconf.RPICameraExposure {
case "normal", "short", "long", "custom":
default:
return fmt.Errorf("invalid 'rpiCameraExposure' value")
}
switch pconf.RPICameraAWB {
case "auto", "incandescent", "tungsten", "fluorescent", "indoor", "daylight", "cloudy", "custom":
default:
return fmt.Errorf("invalid 'rpiCameraAWB' value")
}
switch pconf.RPICameraDenoise {
case "off", "cdn_off", "cdn_fast", "cdn_hq":
default:
return fmt.Errorf("invalid 'rpiCameraDenoise' value")
}
switch pconf.RPICameraMetering {
case "centre", "spot", "matrix", "custom":
default:
return fmt.Errorf("invalid 'rpiCameraMetering' value")
}
switch pconf.RPICameraAfMode {
case "auto", "manual", "continuous":
default:
return fmt.Errorf("invalid 'rpiCameraAfMode' value")
}
switch pconf.RPICameraAfRange {
case "normal", "macro", "full":
default:
return fmt.Errorf("invalid 'rpiCameraAfRange' value")
}
switch pconf.RPICameraAfSpeed {
case "normal", "fast":
default:
return fmt.Errorf("invalid 'rpiCameraAfSpeed' value")
}
// Hooks
if pconf.RunOnInit != "" && pconf.Regexp != nil {

View File

@ -7,13 +7,13 @@ import (
"github.com/bluenviron/gortsplib/v4"
)
// SourceProtocol is the sourceProtocol parameter.
type SourceProtocol struct {
// RTSPTransport is the rtspTransport parameter.
type RTSPTransport struct {
*gortsplib.Transport
}
// MarshalJSON implements json.Marshaler.
func (d SourceProtocol) MarshalJSON() ([]byte, error) {
func (d RTSPTransport) MarshalJSON() ([]byte, error) {
var out string
if d.Transport == nil {
@ -38,7 +38,7 @@ func (d SourceProtocol) MarshalJSON() ([]byte, error) {
}
// UnmarshalJSON implements json.Unmarshaler.
func (d *SourceProtocol) UnmarshalJSON(b []byte) error {
func (d *RTSPTransport) UnmarshalJSON(b []byte) error {
var in string
if err := json.Unmarshal(b, &in); err != nil {
return err
@ -68,6 +68,6 @@ func (d *SourceProtocol) UnmarshalJSON(b []byte) error {
}
// UnmarshalEnv implements env.Unmarshaler.
func (d *SourceProtocol) UnmarshalEnv(_ string, v string) error {
func (d *RTSPTransport) UnmarshalEnv(_ string, v string) error {
return d.UnmarshalJSON([]byte(`"` + v + `"`))
}

View File

@ -81,12 +81,12 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
decodeErrLogger := logger.NewLimitedLogger(s)
c := &gortsplib.Client{
Transport: params.Conf.SourceProtocol.Transport,
Transport: params.Conf.RTSPTransport.Transport,
TLSConfig: tls.ConfigForFingerprint(params.Conf.SourceFingerprint),
ReadTimeout: time.Duration(s.ReadTimeout),
WriteTimeout: time.Duration(s.WriteTimeout),
WriteQueueSize: s.WriteQueueSize,
AnyPortEnable: params.Conf.SourceAnyPortEnable,
AnyPortEnable: params.Conf.RTSPAnyPort,
OnRequest: func(req *base.Request) {
s.Log(logger.Debug, "[c->s] %v", req)
},

View File

@ -210,7 +210,7 @@ func TestRTSPSource(t *testing.T) {
var te *tester.Tester
if source != "tls" {
var sp conf.SourceProtocol
var sp conf.RTSPTransport
sp.UnmarshalJSON([]byte(`"` + source + `"`)) //nolint:errcheck
te = tester.New(
@ -223,8 +223,8 @@ func TestRTSPSource(t *testing.T) {
}
},
&conf.Path{
Source: "rtsp://testuser:testpass@localhost:8555/teststream",
SourceProtocol: sp,
Source: "rtsp://testuser:testpass@localhost:8555/teststream",
RTSPTransport: sp,
},
)
} else {
@ -312,7 +312,7 @@ func TestRTSPSourceNoPassword(t *testing.T) {
stream = gortsplib.NewServerStream(&s, &description.Session{Medias: []*description.Media{testMediaH264}})
defer stream.Close()
var sp conf.SourceProtocol
var sp conf.RTSPTransport
sp.UnmarshalJSON([]byte(`"tcp"`)) //nolint:errcheck
te := tester.New(
@ -325,8 +325,8 @@ func TestRTSPSourceNoPassword(t *testing.T) {
}
},
&conf.Path{
Source: "rtsp://testuser:@127.0.0.1:8555/teststream",
SourceProtocol: sp,
Source: "rtsp://testuser:@127.0.0.1:8555/teststream",
RTSPTransport: sp,
},
)
defer te.Close()

View File

@ -337,9 +337,9 @@ pathDefaults:
###############################################
# Default path settings -> Publisher source (when source is "publisher")
# 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
# if no one is publishing, redirect readers to this path.
# 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
@ -348,18 +348,18 @@ pathDefaults:
###############################################
# Default path settings -> RTSP source (when source is a RTSP or a RTSPS URL)
# protocol used to pull the stream. available values are "automatic", "udp", "multicast", "tcp".
sourceProtocol: automatic
# support sources that don't provide server ports or use random server ports. This is a security issue
# Transport protocol used to pull the stream. available values are "automatic", "udp", "multicast", "tcp".
rtspTransport: automatic
# Support sources that don't provide server ports or use random server ports. This is a security issue
# and must be used only when interacting with sources that require it.
sourceAnyPortEnable: no
# range header to send to the source, in order to start streaming from the specified offset.
rtspAnyPort: no
# Range header to send to the source, in order to start streaming from the specified offset.
# available values:
# * clock: Absolute time
# * npt: Normal Play Time
# * smpte: SMPTE timestamps relative to the start of the recording
rtspRangeType:
# available values:
# Available values:
# * clock: UTC ISO 8601 combined date and time string, e.g. 20230812T120000Z
# * npt: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
# * smpte: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"

View File

@ -28,7 +28,7 @@ paths:
# proxied:
# source: rtsp://192.168.2.198:554/stream
# sourceProtocol: tcp
# rtspTransport: tcp
# sourceOnDemand: yes
# runOnDemand: ffmpeg -i rtsp://192.168.2.198:554/stream -c copy -f rtsp rtsp://localhost:$$RTSP_PORT/proxied2