mirror of
https://github.com/bluenviron/mediamtx
synced 2025-01-11 09:29:35 +00:00
rename sourceProtocol into rtspTransport, sourceAnyPortEnable into rtspAnyPort (#2644)
This commit is contained in:
parent
faf7218fda
commit
1d1d64cb89
@ -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.
|
||||
|
@ -252,9 +252,9 @@ components:
|
||||
type: string
|
||||
|
||||
# RTSP source
|
||||
sourceProtocol:
|
||||
rtspTransport:
|
||||
type: string
|
||||
sourceAnyPortEnable:
|
||||
rtspAnyPort:
|
||||
type: boolean
|
||||
rtspRangeType:
|
||||
type: string
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 + `"`))
|
||||
}
|
@ -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)
|
||||
},
|
||||
|
@ -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()
|
||||
|
16
mediamtx.yml
16
mediamtx.yml
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user