mediamtx/internal/conf/conf.go

344 lines
10 KiB
Go
Raw Normal View History

2022-09-17 19:19:45 +00:00
// Package conf contains the struct that holds the configuration of the software.
2020-10-13 22:50:08 +00:00
package conf
2020-07-12 11:16:33 +00:00
import (
"bytes"
"encoding/json"
"errors"
2020-07-12 11:16:33 +00:00
"fmt"
"os"
"sort"
"strings"
2020-07-12 11:16:33 +00:00
"time"
"github.com/bluenviron/gohlslib"
2023-04-01 16:39:12 +00:00
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/headers"
2023-05-16 14:14:20 +00:00
"github.com/bluenviron/mediamtx/internal/conf/decrypt"
"github.com/bluenviron/mediamtx/internal/conf/env"
"github.com/bluenviron/mediamtx/internal/conf/yaml"
"github.com/bluenviron/mediamtx/internal/logger"
2020-07-12 11:16:33 +00:00
)
func getSortedKeys(paths map[string]*PathConf) []string {
ret := make([]string, len(paths))
i := 0
for name := range paths {
ret[i] = name
i++
}
sort.Strings(ret)
return ret
}
func loadFromFile(fpath string, conf *Conf) (bool, error) {
if fpath == "mediamtx.yml" {
// give priority to the legacy configuration file, in order not to break
// existing setups
if _, err := os.Stat("rtsp-simple-server.yml"); err == nil {
fpath = "rtsp-simple-server.yml"
}
}
// mediamtx.yml is optional
// other configuration files are not
if fpath == "mediamtx.yml" || fpath == "rtsp-simple-server.yml" {
if _, err := os.Stat(fpath); errors.Is(err, os.ErrNotExist) {
conf.UnmarshalJSON(nil) // load defaults
return false, nil
}
}
2022-09-17 19:19:45 +00:00
byts, err := os.ReadFile(fpath)
if err != nil {
return true, err
}
if key, ok := os.LookupEnv("RTSP_CONFKEY"); ok { // legacy format
byts, err = decrypt.Decrypt(key, byts)
if err != nil {
return true, err
}
}
if key, ok := os.LookupEnv("MTX_CONFKEY"); ok {
byts, err = decrypt.Decrypt(key, byts)
if err != nil {
return true, err
}
}
err = yaml.Load(byts, conf)
if err != nil {
return true, err
}
return true, nil
}
func contains(list []headers.AuthMethod, item headers.AuthMethod) bool {
for _, i := range list {
if i == item {
return true
}
}
return false
}
// Conf is a configuration.
type Conf struct {
// general
LogLevel LogLevel `json:"logLevel"`
LogDestinations LogDestinations `json:"logDestinations"`
LogFile string `json:"logFile"`
ReadTimeout StringDuration `json:"readTimeout"`
WriteTimeout StringDuration `json:"writeTimeout"`
ReadBufferCount int `json:"readBufferCount"`
UDPMaxPayloadSize int `json:"udpMaxPayloadSize"`
ExternalAuthenticationURL string `json:"externalAuthenticationURL"`
API bool `json:"api"`
APIAddress string `json:"apiAddress"`
Metrics bool `json:"metrics"`
MetricsAddress string `json:"metricsAddress"`
PPROF bool `json:"pprof"`
PPROFAddress string `json:"pprofAddress"`
RunOnConnect string `json:"runOnConnect"`
RunOnConnectRestart bool `json:"runOnConnectRestart"`
// RTSP
RTSPDisable bool `json:"rtspDisable"`
Protocols Protocols `json:"protocols"`
Encryption Encryption `json:"encryption"`
RTSPAddress string `json:"rtspAddress"`
RTSPSAddress string `json:"rtspsAddress"`
RTPAddress string `json:"rtpAddress"`
RTCPAddress string `json:"rtcpAddress"`
MulticastIPRange string `json:"multicastIPRange"`
MulticastRTPPort int `json:"multicastRTPPort"`
MulticastRTCPPort int `json:"multicastRTCPPort"`
ServerKey string `json:"serverKey"`
ServerCert string `json:"serverCert"`
AuthMethods AuthMethods `json:"authMethods"`
// RTMP
2022-08-16 11:53:04 +00:00
RTMPDisable bool `json:"rtmpDisable"`
RTMPAddress string `json:"rtmpAddress"`
RTMPEncryption Encryption `json:"rtmpEncryption"`
RTMPSAddress string `json:"rtmpsAddress"`
RTMPServerKey string `json:"rtmpServerKey"`
RTMPServerCert string `json:"rtmpServerCert"`
// HLS
HLSDisable bool `json:"hlsDisable"`
HLSAddress string `json:"hlsAddress"`
2022-12-15 23:50:47 +00:00
HLSEncryption bool `json:"hlsEncryption"`
HLSServerKey string `json:"hlsServerKey"`
HLSServerCert string `json:"hlsServerCert"`
HLSAlwaysRemux bool `json:"hlsAlwaysRemux"`
Implement Low-Latency HLS (#938) * add hlsVariant parameter * hls: split muxer into variants * hls: implement fmp4 segments * hls muxer: implement low latency mode * hls muxer: support audio with fmp4 mode * hls muxer: rewrite file router * hls muxer: implement preload hint * hls muxer: add various error codes * hls muxer: use explicit flags * hls muxer: fix error in aac pts * hls muxer: fix sudden freezes with video+audio * hls muxer: skip empty parts * hls muxer: fix video FPS * hls muxer: add parameter hlsPartDuration * hls muxer: refactor fmp4 muxer * hls muxer: fix CAN-SKIP-UNTIL * hls muxer: refactor code * hls muxer: show only parts of last 2 segments * hls muxer: implementa playlist delta updates * hls muxer: change playlist content type * hls muxer: improve video dts precision * hls muxer: fix video sample flags * hls muxer: improve iphone audio support * hls muxer: improve mp4 timestamp precision * hls muxer: add offset between pts and dts * hls muxer: close muxer in case of error * hls muxer: stop logging requests with the info level * hls muxer: rename entry into sample * hls muxer: compensate video dts error over time * hls muxer: change default segment count * hls muxer: add starting gap * hls muxer: set default part duration to 200ms * hls muxer: fix audio-only streams on ios * hls muxer: add playsinline attribute to video tag of default web page * hls muxer: keep mpegts as the default hls variant * hls muxer: implement encryption * hls muxer: rewrite dts estimation * hls muxer: improve DTS precision * hls muxer: use right SPS/PPS for each sample * hls muxer: adjust part duration dynamically * add comments * update readme * hls muxer: fix memory leak * hls muxer: decrease ram consumption
2022-05-31 17:17:26 +00:00
HLSVariant HLSVariant `json:"hlsVariant"`
HLSSegmentCount int `json:"hlsSegmentCount"`
HLSSegmentDuration StringDuration `json:"hlsSegmentDuration"`
Implement Low-Latency HLS (#938) * add hlsVariant parameter * hls: split muxer into variants * hls: implement fmp4 segments * hls muxer: implement low latency mode * hls muxer: support audio with fmp4 mode * hls muxer: rewrite file router * hls muxer: implement preload hint * hls muxer: add various error codes * hls muxer: use explicit flags * hls muxer: fix error in aac pts * hls muxer: fix sudden freezes with video+audio * hls muxer: skip empty parts * hls muxer: fix video FPS * hls muxer: add parameter hlsPartDuration * hls muxer: refactor fmp4 muxer * hls muxer: fix CAN-SKIP-UNTIL * hls muxer: refactor code * hls muxer: show only parts of last 2 segments * hls muxer: implementa playlist delta updates * hls muxer: change playlist content type * hls muxer: improve video dts precision * hls muxer: fix video sample flags * hls muxer: improve iphone audio support * hls muxer: improve mp4 timestamp precision * hls muxer: add offset between pts and dts * hls muxer: close muxer in case of error * hls muxer: stop logging requests with the info level * hls muxer: rename entry into sample * hls muxer: compensate video dts error over time * hls muxer: change default segment count * hls muxer: add starting gap * hls muxer: set default part duration to 200ms * hls muxer: fix audio-only streams on ios * hls muxer: add playsinline attribute to video tag of default web page * hls muxer: keep mpegts as the default hls variant * hls muxer: implement encryption * hls muxer: rewrite dts estimation * hls muxer: improve DTS precision * hls muxer: use right SPS/PPS for each sample * hls muxer: adjust part duration dynamically * add comments * update readme * hls muxer: fix memory leak * hls muxer: decrease ram consumption
2022-05-31 17:17:26 +00:00
HLSPartDuration StringDuration `json:"hlsPartDuration"`
HLSSegmentMaxSize StringSize `json:"hlsSegmentMaxSize"`
HLSAllowOrigin string `json:"hlsAllowOrigin"`
HLSTrustedProxies IPsOrCIDRs `json:"hlsTrustedProxies"`
HLSDirectory string `json:"hlsDirectory"`
2022-12-15 23:50:47 +00:00
// WebRTC
WebRTCDisable bool `json:"webrtcDisable"`
WebRTCAddress string `json:"webrtcAddress"`
WebRTCEncryption bool `json:"webrtcEncryption"`
WebRTCServerKey string `json:"webrtcServerKey"`
WebRTCServerCert string `json:"webrtcServerCert"`
WebRTCAllowOrigin string `json:"webrtcAllowOrigin"`
WebRTCTrustedProxies IPsOrCIDRs `json:"webrtcTrustedProxies"`
WebRTCICEServers []string `json:"webrtcICEServers"` // deprecated
WebRTCICEServers2 []WebRTCICEServer `json:"webrtcICEServers2"`
WebRTCICEHostNAT1To1IPs []string `json:"webrtcICEHostNAT1To1IPs"`
WebRTCICEUDPMuxAddress string `json:"webrtcICEUDPMuxAddress"`
WebRTCICETCPMuxAddress string `json:"webrtcICETCPMuxAddress"`
2022-12-15 23:50:47 +00:00
// paths
Paths map[string]*PathConf `json:"paths"`
}
// Load loads a Conf.
func Load(fpath string) (*Conf, bool, error) {
conf := &Conf{}
found, err := loadFromFile(fpath, conf)
if err != nil {
return nil, false, err
}
err = env.Load("RTSP", conf) // legacy prefix
if err != nil {
return nil, false, err
}
err = env.Load("MTX", conf)
if err != nil {
return nil, false, err
}
err = conf.Check()
if err != nil {
return nil, false, err
}
return conf, found, nil
}
// Clone clones the configuration.
func (conf Conf) Clone() *Conf {
enc, err := json.Marshal(conf)
if err != nil {
panic(err)
}
var dest Conf
err = json.Unmarshal(enc, &dest)
if err != nil {
panic(err)
}
return &dest
}
// Check checks the configuration for errors.
func (conf *Conf) Check() error {
2022-12-15 23:50:47 +00:00
// general
2022-07-05 21:45:57 +00:00
if (conf.ReadBufferCount & (conf.ReadBufferCount - 1)) != 0 {
return fmt.Errorf("'readBufferCount' must be a power of two")
}
if conf.UDPMaxPayloadSize > 1472 {
return fmt.Errorf("'udpMaxPayloadSize' must be less than 1472")
2022-07-05 21:45:57 +00:00
}
if conf.ExternalAuthenticationURL != "" {
if !strings.HasPrefix(conf.ExternalAuthenticationURL, "http://") &&
!strings.HasPrefix(conf.ExternalAuthenticationURL, "https://") {
return fmt.Errorf("'externalAuthenticationURL' must be a HTTP URL")
}
if contains(conf.AuthMethods, headers.AuthDigest) {
return fmt.Errorf("'externalAuthenticationURL' can't be used when 'digest' is in authMethods")
}
}
2022-12-15 23:50:47 +00:00
// RTSP
if conf.Encryption == EncryptionStrict {
2021-10-22 16:41:10 +00:00
if _, ok := conf.Protocols[Protocol(gortsplib.TransportUDP)]; ok {
return fmt.Errorf("strict encryption can't be used with the UDP transport protocol")
}
if _, ok := conf.Protocols[Protocol(gortsplib.TransportUDPMulticast)]; ok {
return fmt.Errorf("strict encryption can't be used with the UDP-multicast transport protocol")
2020-12-13 22:43:31 +00:00
}
}
2022-12-15 23:50:47 +00:00
// WebRTC
for _, server := range conf.WebRTCICEServers {
parts := strings.Split(server, ":")
if len(parts) == 5 {
conf.WebRTCICEServers2 = append(conf.WebRTCICEServers2, WebRTCICEServer{
URL: parts[0] + ":" + parts[3] + ":" + parts[4],
Username: parts[1],
Password: parts[2],
})
} else {
conf.WebRTCICEServers2 = append(conf.WebRTCICEServers2, WebRTCICEServer{
URL: server,
})
}
}
conf.WebRTCICEServers = nil
for _, server := range conf.WebRTCICEServers2 {
if !strings.HasPrefix(server.URL, "stun:") &&
!strings.HasPrefix(server.URL, "turn:") &&
!strings.HasPrefix(server.URL, "turns:") {
return fmt.Errorf("invalid ICE server: '%s'", server.URL)
}
}
// do not add automatically "all", since user may want to
// initialize all paths through API or hot reloading.
if conf.Paths == nil {
conf.Paths = make(map[string]*PathConf)
2020-07-12 11:16:33 +00:00
}
for _, name := range getSortedKeys(conf.Paths) {
pconf := conf.Paths[name]
2020-09-19 15:13:45 +00:00
if pconf == nil {
pconf = &PathConf{}
pconf.UnmarshalJSON(nil) // fill defaults
conf.Paths[name] = pconf
}
err := pconf.check(conf, name)
if err != nil {
return err
2020-07-12 11:16:33 +00:00
}
}
2020-07-12 11:16:33 +00:00
return nil
}
// UnmarshalJSON implements json.Unmarshaler. It is used to set default values.
func (conf *Conf) UnmarshalJSON(b []byte) error {
// general
conf.LogLevel = LogLevel(logger.Info)
conf.LogDestinations = LogDestinations{logger.DestinationStdout}
conf.LogFile = "mediamtx.log"
conf.ReadTimeout = 10 * StringDuration(time.Second)
conf.WriteTimeout = 10 * StringDuration(time.Second)
conf.ReadBufferCount = 512
conf.UDPMaxPayloadSize = 1472
conf.APIAddress = "127.0.0.1:9997"
conf.MetricsAddress = "127.0.0.1:9998"
conf.PPROFAddress = "127.0.0.1:9999"
// RTSP
conf.Protocols = Protocols{
Protocol(gortsplib.TransportUDP): {},
Protocol(gortsplib.TransportUDPMulticast): {},
Protocol(gortsplib.TransportTCP): {},
}
conf.RTSPAddress = ":8554"
conf.RTSPSAddress = ":8322"
conf.RTPAddress = ":8000"
conf.RTCPAddress = ":8001"
conf.MulticastIPRange = "224.1.0.0/16"
conf.MulticastRTPPort = 8002
conf.MulticastRTCPPort = 8003
conf.ServerKey = "server.key"
conf.ServerCert = "server.crt"
conf.AuthMethods = AuthMethods{headers.AuthBasic}
// RTMP
conf.RTMPAddress = ":1935"
conf.RTMPSAddress = ":1936"
// HLS
conf.HLSAddress = ":8888"
conf.HLSServerKey = "server.key"
conf.HLSServerCert = "server.crt"
conf.HLSVariant = HLSVariant(gohlslib.MuxerVariantLowLatency)
conf.HLSSegmentCount = 7
conf.HLSSegmentDuration = 1 * StringDuration(time.Second)
conf.HLSPartDuration = 200 * StringDuration(time.Millisecond)
conf.HLSSegmentMaxSize = 50 * 1024 * 1024
conf.HLSAllowOrigin = "*"
// WebRTC
conf.WebRTCAddress = ":8889"
conf.WebRTCServerKey = "server.key"
conf.WebRTCServerCert = "server.crt"
conf.WebRTCAllowOrigin = "*"
conf.WebRTCICEServers2 = []WebRTCICEServer{{URL: "stun:stun.l.google.com:19302"}}
type alias Conf
d := json.NewDecoder(bytes.NewReader(b))
d.DisallowUnknownFields()
return d.Decode((*alias)(conf))
}