When changing configuration in parallel by editing the configuration file and calling the API, the server could get into a deadlock that prevented any further action. This is fixed.
1453 lines
44 KiB
Go
1453 lines
44 KiB
Go
// Package core contains the main struct of the software.
|
|
package core
|
|
|
|
import (
|
|
"context"
|
|
_ "embed"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"reflect"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"slices"
|
|
"strings"
|
|
"sync/atomic"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/alecthomas/kong"
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/api"
|
|
"github.com/bluenviron/mediamtx/internal/auth"
|
|
"github.com/bluenviron/mediamtx/internal/conf"
|
|
"github.com/bluenviron/mediamtx/internal/confwatcher"
|
|
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
|
"github.com/bluenviron/mediamtx/internal/logger"
|
|
"github.com/bluenviron/mediamtx/internal/metrics"
|
|
"github.com/bluenviron/mediamtx/internal/playback"
|
|
"github.com/bluenviron/mediamtx/internal/pprof"
|
|
"github.com/bluenviron/mediamtx/internal/recordcleaner"
|
|
"github.com/bluenviron/mediamtx/internal/rlimit"
|
|
"github.com/bluenviron/mediamtx/internal/servers/hls"
|
|
"github.com/bluenviron/mediamtx/internal/servers/moq"
|
|
"github.com/bluenviron/mediamtx/internal/servers/rtmp"
|
|
"github.com/bluenviron/mediamtx/internal/servers/rtsp"
|
|
"github.com/bluenviron/mediamtx/internal/servers/srt"
|
|
"github.com/bluenviron/mediamtx/internal/servers/webrtc"
|
|
"github.com/bluenviron/mediamtx/internal/upgrade"
|
|
)
|
|
|
|
//go:generate go run ./versiongetter
|
|
|
|
//go:embed VERSION
|
|
var version []byte
|
|
|
|
var started = time.Now()
|
|
|
|
var defaultConfPaths = []string{
|
|
"rtsp-simple-server.yml",
|
|
"mediamtx.yml",
|
|
}
|
|
|
|
var defaultConfPathsNotWin = []string{
|
|
"/usr/local/etc/mediamtx.yml",
|
|
"/usr/etc/mediamtx.yml",
|
|
"/etc/mediamtx/mediamtx.yml",
|
|
}
|
|
|
|
func goArm() string {
|
|
bi, _ := debug.ReadBuildInfo()
|
|
for _, bs := range bi.Settings {
|
|
if bs.Key == "GOARM" {
|
|
return bs.Value
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func getArch() string {
|
|
var arch string
|
|
if runtime.GOARCH == "arm" {
|
|
arch = "armv" + goArm()
|
|
} else {
|
|
arch = runtime.GOARCH
|
|
}
|
|
return arch
|
|
}
|
|
|
|
func atLeastOneRecordDeleteAfter(pathConfs map[string]*conf.Path) bool {
|
|
for _, e := range pathConfs {
|
|
if e.RecordDeleteAfter != 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func getRTPMaxPayloadSize(udpMaxPayloadSize int, rtspEncryption conf.Encryption) int {
|
|
// UDP max payload size - 12 (RTP header)
|
|
v := udpMaxPayloadSize - 12
|
|
|
|
// 10 (SRTP HMAC SHA1 authentication tag)
|
|
if rtspEncryption == conf.EncryptionOptional || rtspEncryption == conf.EncryptionStrict {
|
|
v -= 10
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
func supportsIPv6() bool {
|
|
ln, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6unspecified, Port: 0})
|
|
if err != nil {
|
|
return false
|
|
}
|
|
defer ln.Close() //nolint:errcheck
|
|
|
|
return true
|
|
}
|
|
|
|
var cli struct {
|
|
Confpath string `arg:"" default:""`
|
|
Version bool `help:"print version"`
|
|
CheckVersion bool `help:"check whether a new version is available"`
|
|
Upgrade bool `help:"upgrade executable to the latest version"`
|
|
}
|
|
|
|
type configGlobalPatchReq struct {
|
|
conf conf.OptionalGlobal
|
|
res chan error
|
|
}
|
|
|
|
type configPathDefaultsPatchReq struct {
|
|
conf conf.OptionalPath
|
|
res chan error
|
|
}
|
|
|
|
type configPathAddReq struct {
|
|
name string
|
|
conf conf.OptionalPath
|
|
res chan error
|
|
}
|
|
|
|
type configPathPatchReq struct {
|
|
name string
|
|
conf conf.OptionalPath
|
|
res chan error
|
|
}
|
|
|
|
type configPathReplaceReq struct {
|
|
name string
|
|
conf conf.OptionalPath
|
|
res chan error
|
|
}
|
|
|
|
type configPathDeleteReq struct {
|
|
name string
|
|
res chan error
|
|
}
|
|
|
|
// Core is an instance of MediaMTX.
|
|
type Core struct {
|
|
ctx context.Context
|
|
ctxCancel func()
|
|
confPath string
|
|
conf atomic.Pointer[conf.Conf]
|
|
supportsIPv6 bool
|
|
logger *logger.Logger
|
|
externalCmdPool *externalcmd.Pool
|
|
authManager *auth.Manager
|
|
metrics *metrics.Metrics
|
|
pprof *pprof.PPROF
|
|
recordCleaner *recordcleaner.Cleaner
|
|
playbackServer *playback.Server
|
|
pathManager *pathManager
|
|
rtspServer *rtsp.Server
|
|
rtspsServer *rtsp.Server
|
|
rtmpServer *rtmp.Server
|
|
rtmpsServer *rtmp.Server
|
|
hlsServer *hls.Server
|
|
webRTCServer *webrtc.Server
|
|
srtServer *srt.Server
|
|
moqServer *moq.Server
|
|
api *api.API
|
|
confWatcher *confwatcher.ConfWatcher
|
|
|
|
// in
|
|
chAPIConfigGlobalPatch chan configGlobalPatchReq
|
|
chAPIConfigPathDefaultsPatch chan configPathDefaultsPatchReq
|
|
chAPIConfigPathAdd chan configPathAddReq
|
|
chAPIConfigPathPatch chan configPathPatchReq
|
|
chAPIConfigPathReplace chan configPathReplaceReq
|
|
chAPIConfigPathDelete chan configPathDeleteReq
|
|
|
|
// out
|
|
done chan struct{}
|
|
}
|
|
|
|
// New allocates a Core.
|
|
func New(args []string) (*Core, bool) {
|
|
parser, err := kong.New(&cli,
|
|
kong.Description("MediaMTX "+string(version)+", "+runtime.GOOS+", "+getArch()),
|
|
kong.UsageOnError(),
|
|
kong.ValueFormatter(func(value *kong.Value) string {
|
|
switch value.Name {
|
|
case "confpath":
|
|
return "path to a config file. The default is mediamtx.yml."
|
|
|
|
default:
|
|
return kong.DefaultHelpValueFormatter(value)
|
|
}
|
|
}))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
_, err = parser.Parse(args)
|
|
parser.FatalIfErrorf(err)
|
|
|
|
if cli.Version {
|
|
fmt.Println(string(version))
|
|
os.Exit(0)
|
|
}
|
|
|
|
if cli.CheckVersion {
|
|
var newVersionAvailable bool
|
|
newVersionAvailable, err = upgrade.CheckVersion(string(version), getArch())
|
|
if err != nil {
|
|
fmt.Printf("ERR: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
if newVersionAvailable {
|
|
os.Exit(2)
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
|
|
if cli.Upgrade {
|
|
err = upgrade.Upgrade(string(version), getArch())
|
|
if err != nil {
|
|
fmt.Printf("ERR: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
|
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
|
|
|
p := &Core{
|
|
ctx: ctx,
|
|
ctxCancel: ctxCancel,
|
|
chAPIConfigGlobalPatch: make(chan configGlobalPatchReq),
|
|
chAPIConfigPathDefaultsPatch: make(chan configPathDefaultsPatchReq),
|
|
chAPIConfigPathAdd: make(chan configPathAddReq),
|
|
chAPIConfigPathPatch: make(chan configPathPatchReq),
|
|
chAPIConfigPathReplace: make(chan configPathReplaceReq),
|
|
chAPIConfigPathDelete: make(chan configPathDeleteReq),
|
|
done: make(chan struct{}),
|
|
}
|
|
|
|
tempLogger := &logger.Logger{
|
|
Level: logger.Warn,
|
|
Destinations: []logger.Destination{logger.DestinationStdout},
|
|
Structured: false,
|
|
File: "",
|
|
SysLogPrefix: "",
|
|
}
|
|
tempLogger.Initialize() //nolint:errcheck
|
|
|
|
confPaths := append([]string(nil), defaultConfPaths...)
|
|
if runtime.GOOS != "windows" {
|
|
confPaths = append(confPaths, defaultConfPathsNotWin...)
|
|
}
|
|
|
|
loadedConf, confPath, err := conf.Load(cli.Confpath, confPaths, tempLogger)
|
|
if err != nil {
|
|
fmt.Printf("ERR: %s\n", err)
|
|
return nil, false
|
|
}
|
|
|
|
p.confPath = confPath
|
|
p.conf.Store(loadedConf)
|
|
|
|
err = p.createResources(true)
|
|
if err != nil {
|
|
if p.logger != nil {
|
|
p.Log(logger.Error, "%s", err)
|
|
} else {
|
|
fmt.Printf("ERR: %s\n", err)
|
|
}
|
|
p.closeResources(nil)
|
|
return nil, false
|
|
}
|
|
|
|
go p.run()
|
|
|
|
return p, true
|
|
}
|
|
|
|
// Close closes Core and waits for all goroutines to return.
|
|
func (p *Core) Close() {
|
|
p.ctxCancel()
|
|
<-p.done
|
|
}
|
|
|
|
// Wait waits for the Core to exit.
|
|
func (p *Core) Wait() {
|
|
<-p.done
|
|
}
|
|
|
|
// Log implements logger.Writer.
|
|
func (p *Core) Log(level logger.Level, format string, args ...any) {
|
|
p.logger.Log(level, format, args...)
|
|
}
|
|
|
|
func (p *Core) run() {
|
|
defer close(p.done)
|
|
|
|
confChanged := func() chan struct{} {
|
|
if p.confWatcher != nil {
|
|
return p.confWatcher.Watch()
|
|
}
|
|
return make(chan struct{})
|
|
}()
|
|
|
|
interrupt := make(chan os.Signal, 1)
|
|
signal.Notify(interrupt, os.Interrupt)
|
|
if runtime.GOOS == "linux" {
|
|
signal.Notify(interrupt, syscall.SIGTERM)
|
|
}
|
|
|
|
outer:
|
|
for {
|
|
select {
|
|
case <-confChanged:
|
|
p.Log(logger.Info, "reloading configuration (file changed)")
|
|
|
|
newConf, _, err := conf.Load(p.confPath, nil, p.logger)
|
|
if err != nil {
|
|
p.Log(logger.Error, "%s", err)
|
|
break outer
|
|
}
|
|
|
|
err = p.reloadConf(newConf)
|
|
if err != nil {
|
|
p.Log(logger.Error, "%s", err)
|
|
break outer
|
|
}
|
|
|
|
case req := <-p.chAPIConfigGlobalPatch:
|
|
newConf, err := p.doAPIConfigGlobalPatch(req.conf)
|
|
req.res <- err
|
|
|
|
if err == nil {
|
|
err = p.reloadConf(newConf)
|
|
if err != nil {
|
|
p.Log(logger.Error, "%s", err)
|
|
break outer
|
|
}
|
|
}
|
|
|
|
case req := <-p.chAPIConfigPathDefaultsPatch:
|
|
newConf, err := p.doAPIConfigPathDefaultsPatch(req.conf)
|
|
req.res <- err
|
|
|
|
if err == nil {
|
|
err = p.reloadConf(newConf)
|
|
if err != nil {
|
|
p.Log(logger.Error, "%s", err)
|
|
break outer
|
|
}
|
|
}
|
|
|
|
case req := <-p.chAPIConfigPathAdd:
|
|
newConf, err := p.doAPIConfigPathAdd(req.name, req.conf)
|
|
req.res <- err
|
|
|
|
if err == nil {
|
|
err = p.reloadConf(newConf)
|
|
if err != nil {
|
|
p.Log(logger.Error, "%s", err)
|
|
break outer
|
|
}
|
|
}
|
|
|
|
case req := <-p.chAPIConfigPathPatch:
|
|
newConf, err := p.doAPIConfigPathPatch(req.name, req.conf)
|
|
req.res <- err
|
|
|
|
if err == nil {
|
|
err = p.reloadConf(newConf)
|
|
if err != nil {
|
|
p.Log(logger.Error, "%s", err)
|
|
break outer
|
|
}
|
|
}
|
|
|
|
case req := <-p.chAPIConfigPathReplace:
|
|
newConf, err := p.doAPIConfigPathReplace(req.name, req.conf)
|
|
req.res <- err
|
|
|
|
if err == nil {
|
|
err = p.reloadConf(newConf)
|
|
if err != nil {
|
|
p.Log(logger.Error, "%s", err)
|
|
break outer
|
|
}
|
|
}
|
|
|
|
case req := <-p.chAPIConfigPathDelete:
|
|
newConf, err := p.doAPIConfigPathDelete(req.name)
|
|
req.res <- err
|
|
|
|
if err == nil {
|
|
err = p.reloadConf(newConf)
|
|
if err != nil {
|
|
p.Log(logger.Error, "%s", err)
|
|
break outer
|
|
}
|
|
}
|
|
|
|
case <-interrupt:
|
|
p.Log(logger.Info, "shutting down gracefully")
|
|
break outer
|
|
|
|
case <-p.ctx.Done():
|
|
break outer
|
|
}
|
|
}
|
|
|
|
p.ctxCancel()
|
|
|
|
p.closeResources(nil)
|
|
}
|
|
|
|
func (p *Core) createResources(initial bool) error {
|
|
currentConf := p.conf.Load()
|
|
var err error
|
|
|
|
if p.logger == nil {
|
|
i := &logger.Logger{
|
|
Level: logger.Level(currentConf.LogLevel),
|
|
Destinations: currentConf.LogDestinations.ToDestinations(),
|
|
Structured: currentConf.LogStructured,
|
|
File: currentConf.LogFile,
|
|
SysLogPrefix: currentConf.SysLogPrefix,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.logger = i
|
|
}
|
|
|
|
if initial {
|
|
p.Log(logger.Info, "MediaMTX %s, %s, %s", string(version), runtime.GOOS, getArch())
|
|
|
|
if p.confPath != "" {
|
|
a, _ := filepath.Abs(p.confPath)
|
|
p.Log(logger.Info, "configuration loaded from %s", a)
|
|
} else {
|
|
list := make([]string, len(defaultConfPaths))
|
|
for i, pa := range defaultConfPaths {
|
|
a, _ := filepath.Abs(pa)
|
|
list[i] = a
|
|
}
|
|
|
|
p.Log(logger.Warn,
|
|
"configuration file not found (looked in %s), using an empty configuration",
|
|
strings.Join(list, ", "))
|
|
}
|
|
|
|
// on Linux, try to raise the number of file descriptors that can be opened
|
|
// to allow the maximum possible number of clients.
|
|
rlimit.Raise() //nolint:errcheck
|
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
|
|
p.supportsIPv6 = supportsIPv6()
|
|
|
|
p.externalCmdPool = &externalcmd.Pool{}
|
|
p.externalCmdPool.Initialize()
|
|
}
|
|
|
|
if p.authManager == nil {
|
|
p.authManager = &auth.Manager{
|
|
Method: currentConf.AuthMethod,
|
|
InternalUsers: currentConf.AuthInternalUsers,
|
|
HTTPAddress: currentConf.AuthHTTPAddress,
|
|
HTTPFingerprint: currentConf.AuthHTTPFingerprint,
|
|
HTTPExclude: currentConf.AuthHTTPExclude,
|
|
JWTJWKS: currentConf.AuthJWTJWKS,
|
|
JWTJWKSFingerprint: currentConf.AuthJWTJWKSFingerprint,
|
|
JWTClaimKey: currentConf.AuthJWTClaimKey,
|
|
JWTExclude: currentConf.AuthJWTExclude,
|
|
JWTInHTTPQuery: currentConf.AuthJWTInHTTPQuery,
|
|
JWTIssuer: currentConf.AuthJWTIssuer,
|
|
JWTAudience: currentConf.AuthJWTAudience,
|
|
ReadTimeout: time.Duration(currentConf.ReadTimeout),
|
|
}
|
|
}
|
|
|
|
if currentConf.Metrics &&
|
|
p.metrics == nil {
|
|
i := &metrics.Metrics{
|
|
Address: currentConf.MetricsAddress,
|
|
DumpPackets: currentConf.DumpPackets,
|
|
Encryption: currentConf.MetricsEncryption,
|
|
ServerKey: currentConf.MetricsServerKey,
|
|
ServerCert: currentConf.MetricsServerCert,
|
|
AllowOrigins: currentConf.MetricsAllowOrigins,
|
|
TrustedProxies: currentConf.MetricsTrustedProxies,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
AuthManager: p.authManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.metrics = i
|
|
}
|
|
|
|
if currentConf.PPROF &&
|
|
p.pprof == nil {
|
|
i := &pprof.PPROF{
|
|
Address: currentConf.PPROFAddress,
|
|
DumpPackets: currentConf.DumpPackets,
|
|
Encryption: currentConf.PPROFEncryption,
|
|
ServerKey: currentConf.PPROFServerKey,
|
|
ServerCert: currentConf.PPROFServerCert,
|
|
AllowOrigins: currentConf.PPROFAllowOrigins,
|
|
TrustedProxies: currentConf.PPROFTrustedProxies,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
AuthManager: p.authManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.pprof = i
|
|
}
|
|
|
|
if p.recordCleaner == nil &&
|
|
atLeastOneRecordDeleteAfter(currentConf.Paths) {
|
|
p.recordCleaner = &recordcleaner.Cleaner{
|
|
PathConfs: currentConf.Paths,
|
|
Parent: p,
|
|
}
|
|
p.recordCleaner.Initialize()
|
|
}
|
|
|
|
if currentConf.Playback &&
|
|
p.playbackServer == nil {
|
|
i := &playback.Server{
|
|
Address: currentConf.PlaybackAddress,
|
|
DumpPackets: currentConf.DumpPackets,
|
|
Encryption: currentConf.PlaybackEncryption,
|
|
ServerKey: currentConf.PlaybackServerKey,
|
|
ServerCert: currentConf.PlaybackServerCert,
|
|
AllowOrigins: currentConf.PlaybackAllowOrigins,
|
|
TrustedProxies: currentConf.PlaybackTrustedProxies,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
PathConfs: currentConf.Paths,
|
|
AuthManager: p.authManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.playbackServer = i
|
|
}
|
|
|
|
if p.pathManager == nil {
|
|
rtpMaxPayloadSize := getRTPMaxPayloadSize(currentConf.UDPMaxPayloadSize, currentConf.RTSPEncryption)
|
|
|
|
p.pathManager = &pathManager{
|
|
logLevel: currentConf.LogLevel,
|
|
dumpPackets: currentConf.DumpPackets,
|
|
rtspAddress: currentConf.RTSPAddress,
|
|
readTimeout: currentConf.ReadTimeout,
|
|
writeTimeout: currentConf.WriteTimeout,
|
|
writeQueueSize: currentConf.WriteQueueSize,
|
|
udpReadBufferSize: currentConf.UDPReadBufferSize,
|
|
udpMaxPayloadSize: currentConf.UDPMaxPayloadSize,
|
|
rtpMaxPayloadSize: rtpMaxPayloadSize,
|
|
supportsIPv6: p.supportsIPv6,
|
|
pathConfs: currentConf.Paths,
|
|
authManager: p.authManager,
|
|
externalCmdPool: p.externalCmdPool,
|
|
metrics: p.metrics,
|
|
parent: p,
|
|
}
|
|
p.pathManager.initialize()
|
|
}
|
|
|
|
if currentConf.RTSP &&
|
|
(currentConf.RTSPEncryption == conf.EncryptionNo ||
|
|
currentConf.RTSPEncryption == conf.EncryptionOptional) &&
|
|
p.rtspServer == nil {
|
|
udpReadBufferSize := currentConf.UDPReadBufferSize
|
|
if currentConf.RTSPUDPReadBufferSize != nil {
|
|
udpReadBufferSize = *currentConf.RTSPUDPReadBufferSize
|
|
}
|
|
|
|
i := &rtsp.Server{
|
|
Address: currentConf.RTSPAddress,
|
|
AuthMethods: currentConf.RTSPAuthMethods.ToAuthMethods(),
|
|
DumpPackets: currentConf.DumpPackets,
|
|
UDPReadBufferSize: udpReadBufferSize,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
WriteQueueSize: currentConf.WriteQueueSize,
|
|
RTSPTransports: currentConf.RTSPTransports,
|
|
RTPAddress: currentConf.RTPAddress,
|
|
RTCPAddress: currentConf.RTCPAddress,
|
|
MulticastIPRange: currentConf.MulticastIPRange,
|
|
MulticastRTPPort: currentConf.MulticastRTPPort,
|
|
MulticastRTCPPort: currentConf.MulticastRTCPPort,
|
|
Encryption: false,
|
|
ServerCert: "",
|
|
ServerKey: "",
|
|
RTSPAddress: currentConf.RTSPAddress,
|
|
TrustedProxies: currentConf.RTSPTrustedProxies,
|
|
Transports: currentConf.RTSPTransports,
|
|
RunOnConnect: currentConf.RunOnConnect,
|
|
RunOnConnectRestart: currentConf.RunOnConnectRestart,
|
|
RunOnDisconnect: currentConf.RunOnDisconnect,
|
|
ExternalCmdPool: p.externalCmdPool,
|
|
Metrics: p.metrics,
|
|
PathManager: p.pathManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.rtspServer = i
|
|
}
|
|
|
|
if currentConf.RTSP &&
|
|
(currentConf.RTSPEncryption == conf.EncryptionStrict ||
|
|
currentConf.RTSPEncryption == conf.EncryptionOptional) &&
|
|
p.rtspsServer == nil {
|
|
udpReadBufferSize := currentConf.UDPReadBufferSize
|
|
if currentConf.RTSPUDPReadBufferSize != nil {
|
|
udpReadBufferSize = *currentConf.RTSPUDPReadBufferSize
|
|
}
|
|
|
|
i := &rtsp.Server{
|
|
Address: currentConf.RTSPSAddress,
|
|
AuthMethods: currentConf.RTSPAuthMethods.ToAuthMethods(),
|
|
DumpPackets: currentConf.DumpPackets,
|
|
UDPReadBufferSize: udpReadBufferSize,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
WriteQueueSize: currentConf.WriteQueueSize,
|
|
RTSPTransports: currentConf.RTSPTransports,
|
|
RTPAddress: currentConf.SRTPAddress,
|
|
RTCPAddress: currentConf.SRTCPAddress,
|
|
MulticastIPRange: currentConf.MulticastIPRange,
|
|
MulticastRTPPort: currentConf.MulticastSRTPPort,
|
|
MulticastRTCPPort: currentConf.MulticastSRTCPPort,
|
|
Encryption: true,
|
|
ServerCert: currentConf.RTSPServerCert,
|
|
ServerKey: currentConf.RTSPServerKey,
|
|
RTSPAddress: currentConf.RTSPAddress,
|
|
TrustedProxies: currentConf.RTSPTrustedProxies,
|
|
Transports: currentConf.RTSPTransports,
|
|
RunOnConnect: currentConf.RunOnConnect,
|
|
RunOnConnectRestart: currentConf.RunOnConnectRestart,
|
|
RunOnDisconnect: currentConf.RunOnDisconnect,
|
|
ExternalCmdPool: p.externalCmdPool,
|
|
Metrics: p.metrics,
|
|
PathManager: p.pathManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.rtspsServer = i
|
|
}
|
|
|
|
if currentConf.RTMP &&
|
|
(currentConf.RTMPEncryption == conf.EncryptionNo ||
|
|
currentConf.RTMPEncryption == conf.EncryptionOptional) &&
|
|
p.rtmpServer == nil {
|
|
i := &rtmp.Server{
|
|
Address: currentConf.RTMPAddress,
|
|
DumpPackets: currentConf.DumpPackets,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
Encryption: false,
|
|
ServerCert: "",
|
|
ServerKey: "",
|
|
RTSPAddress: currentConf.RTSPAddress,
|
|
TrustedProxies: currentConf.RTMPTrustedProxies,
|
|
RunOnConnect: currentConf.RunOnConnect,
|
|
RunOnConnectRestart: currentConf.RunOnConnectRestart,
|
|
RunOnDisconnect: currentConf.RunOnDisconnect,
|
|
ExternalCmdPool: p.externalCmdPool,
|
|
Metrics: p.metrics,
|
|
PathManager: p.pathManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.rtmpServer = i
|
|
}
|
|
|
|
if currentConf.RTMP &&
|
|
(currentConf.RTMPEncryption == conf.EncryptionStrict ||
|
|
currentConf.RTMPEncryption == conf.EncryptionOptional) &&
|
|
p.rtmpsServer == nil {
|
|
i := &rtmp.Server{
|
|
Address: currentConf.RTMPSAddress,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
Encryption: true,
|
|
ServerCert: currentConf.RTMPServerCert,
|
|
ServerKey: currentConf.RTMPServerKey,
|
|
DumpPackets: currentConf.DumpPackets,
|
|
RTSPAddress: currentConf.RTSPAddress,
|
|
TrustedProxies: currentConf.RTMPTrustedProxies,
|
|
RunOnConnect: currentConf.RunOnConnect,
|
|
RunOnConnectRestart: currentConf.RunOnConnectRestart,
|
|
RunOnDisconnect: currentConf.RunOnDisconnect,
|
|
ExternalCmdPool: p.externalCmdPool,
|
|
Metrics: p.metrics,
|
|
PathManager: p.pathManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.rtmpsServer = i
|
|
}
|
|
|
|
if currentConf.HLS &&
|
|
p.hlsServer == nil {
|
|
i := &hls.Server{
|
|
Address: currentConf.HLSAddress,
|
|
DumpPackets: currentConf.DumpPackets,
|
|
Encryption: currentConf.HLSEncryption,
|
|
ServerKey: currentConf.HLSServerKey,
|
|
ServerCert: currentConf.HLSServerCert,
|
|
AllowOrigins: currentConf.HLSAllowOrigins,
|
|
TrustedProxies: currentConf.HLSTrustedProxies,
|
|
AlwaysRemux: currentConf.HLSAlwaysRemux,
|
|
Variant: currentConf.HLSVariant,
|
|
SegmentCount: currentConf.HLSSegmentCount,
|
|
SegmentDuration: currentConf.HLSSegmentDuration,
|
|
PartDuration: currentConf.HLSPartDuration,
|
|
SegmentMaxSize: currentConf.HLSSegmentMaxSize,
|
|
Directory: currentConf.HLSDirectory,
|
|
CDNSecret: currentConf.HLSCDNSecret,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
MuxerCloseAfter: currentConf.HLSMuxerCloseAfter,
|
|
ExternalCmdPool: p.externalCmdPool,
|
|
Metrics: p.metrics,
|
|
PathManager: p.pathManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.hlsServer = i
|
|
}
|
|
|
|
if currentConf.WebRTC &&
|
|
p.webRTCServer == nil {
|
|
i := &webrtc.Server{
|
|
Address: currentConf.WebRTCAddress,
|
|
DumpPackets: currentConf.DumpPackets,
|
|
Encryption: currentConf.WebRTCEncryption,
|
|
ServerKey: currentConf.WebRTCServerKey,
|
|
ServerCert: currentConf.WebRTCServerCert,
|
|
AllowOrigins: currentConf.WebRTCAllowOrigins,
|
|
TrustedProxies: currentConf.WebRTCTrustedProxies,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
UDPReadBufferSize: currentConf.UDPReadBufferSize,
|
|
LocalUDPAddress: currentConf.WebRTCLocalUDPAddress,
|
|
LocalTCPAddress: currentConf.WebRTCLocalTCPAddress,
|
|
SupportsIPv6: p.supportsIPv6,
|
|
IPsFromInterfaces: currentConf.WebRTCIPsFromInterfaces,
|
|
IPsFromInterfacesList: currentConf.WebRTCIPsFromInterfacesList,
|
|
AdditionalHosts: currentConf.WebRTCAdditionalHosts,
|
|
ICEServers: currentConf.WebRTCICEServers2,
|
|
STUNGatherTimeout: currentConf.WebRTCSTUNGatherTimeout,
|
|
HandshakeTimeout: currentConf.WebRTCHandshakeTimeout,
|
|
TrackGatherTimeout: currentConf.WebRTCTrackGatherTimeout,
|
|
ExternalCmdPool: p.externalCmdPool,
|
|
Metrics: p.metrics,
|
|
PathManager: p.pathManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.webRTCServer = i
|
|
}
|
|
|
|
if currentConf.SRT &&
|
|
p.srtServer == nil {
|
|
i := &srt.Server{
|
|
Address: currentConf.SRTAddress,
|
|
RTSPAddress: currentConf.RTSPAddress,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
UDPMaxPayloadSize: currentConf.UDPMaxPayloadSize,
|
|
UDPReadBufferSize: currentConf.UDPReadBufferSize,
|
|
RunOnConnect: currentConf.RunOnConnect,
|
|
RunOnConnectRestart: currentConf.RunOnConnectRestart,
|
|
RunOnDisconnect: currentConf.RunOnDisconnect,
|
|
ExternalCmdPool: p.externalCmdPool,
|
|
Metrics: p.metrics,
|
|
PathManager: p.pathManager,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.srtServer = i
|
|
}
|
|
|
|
if currentConf.MoQ &&
|
|
p.moqServer == nil {
|
|
i := &moq.Server{
|
|
HTTP2Address: currentConf.MoQHTTP2Address,
|
|
HTTP3Address: currentConf.MoQHTTP3Address,
|
|
QUICAddress: currentConf.MoQQUICAddress,
|
|
ServerKey: currentConf.MoQServerKey,
|
|
ServerCert: currentConf.MoQServerCert,
|
|
AllowOrigins: currentConf.MoQAllowOrigins,
|
|
TrustedProxies: currentConf.MoQTrustedProxies,
|
|
UDPReadBufferSize: currentConf.UDPReadBufferSize,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
PathManager: p.pathManager,
|
|
Metrics: p.metrics,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.moqServer = i
|
|
}
|
|
|
|
if currentConf.API &&
|
|
p.api == nil {
|
|
i := &api.API{
|
|
Version: string(version),
|
|
Started: started,
|
|
Address: currentConf.APIAddress,
|
|
DumpPackets: currentConf.DumpPackets,
|
|
Encryption: currentConf.APIEncryption,
|
|
ServerKey: currentConf.APIServerKey,
|
|
ServerCert: currentConf.APIServerCert,
|
|
AllowOrigins: currentConf.APIAllowOrigins,
|
|
TrustedProxies: currentConf.APITrustedProxies,
|
|
ReadTimeout: currentConf.ReadTimeout,
|
|
WriteTimeout: currentConf.WriteTimeout,
|
|
AuthManager: p.authManager,
|
|
PathManager: p.pathManager,
|
|
RTSPServer: p.rtspServer,
|
|
RTSPSServer: p.rtspsServer,
|
|
RTMPServer: p.rtmpServer,
|
|
RTMPSServer: p.rtmpsServer,
|
|
HLSServer: p.hlsServer,
|
|
WebRTCServer: p.webRTCServer,
|
|
SRTServer: p.srtServer,
|
|
MoQServer: p.moqServer,
|
|
Parent: p,
|
|
}
|
|
err = i.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.api = i
|
|
}
|
|
|
|
if initial && p.confPath != "" {
|
|
cf := &confwatcher.ConfWatcher{FilePath: p.confPath}
|
|
err = cf.Initialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.confWatcher = cf
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Core) closeResources(newConf *conf.Conf) {
|
|
currentConf := p.conf.Load()
|
|
|
|
closeLogger := newConf == nil ||
|
|
newConf.LogLevel != currentConf.LogLevel ||
|
|
!reflect.DeepEqual(newConf.LogDestinations, currentConf.LogDestinations) ||
|
|
newConf.LogFile != currentConf.LogFile ||
|
|
newConf.SysLogPrefix != currentConf.SysLogPrefix ||
|
|
newConf.LogStructured != currentConf.LogStructured
|
|
|
|
closeAuthManager := newConf == nil ||
|
|
newConf.AuthMethod != currentConf.AuthMethod ||
|
|
newConf.AuthHTTPAddress != currentConf.AuthHTTPAddress ||
|
|
newConf.AuthHTTPFingerprint != currentConf.AuthHTTPFingerprint ||
|
|
!reflect.DeepEqual(newConf.AuthHTTPExclude, currentConf.AuthHTTPExclude) ||
|
|
newConf.AuthJWTJWKS != currentConf.AuthJWTJWKS ||
|
|
newConf.AuthJWTJWKSFingerprint != currentConf.AuthJWTJWKSFingerprint ||
|
|
newConf.AuthJWTClaimKey != currentConf.AuthJWTClaimKey ||
|
|
!reflect.DeepEqual(newConf.AuthJWTExclude, currentConf.AuthJWTExclude) ||
|
|
!reflect.DeepEqual(newConf.AuthJWTInHTTPQuery, currentConf.AuthJWTInHTTPQuery) ||
|
|
newConf.AuthJWTIssuer != currentConf.AuthJWTIssuer ||
|
|
newConf.AuthJWTAudience != currentConf.AuthJWTAudience ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout
|
|
if !closeAuthManager && !reflect.DeepEqual(newConf.AuthInternalUsers, currentConf.AuthInternalUsers) {
|
|
p.authManager.ReloadInternalUsers(newConf.AuthInternalUsers)
|
|
}
|
|
|
|
closeMetrics := newConf == nil ||
|
|
newConf.Metrics != currentConf.Metrics ||
|
|
newConf.MetricsAddress != currentConf.MetricsAddress ||
|
|
newConf.MetricsEncryption != currentConf.MetricsEncryption ||
|
|
newConf.MetricsServerKey != currentConf.MetricsServerKey ||
|
|
newConf.MetricsServerCert != currentConf.MetricsServerCert ||
|
|
!slices.Equal(newConf.MetricsAllowOrigins, currentConf.MetricsAllowOrigins) ||
|
|
!reflect.DeepEqual(newConf.MetricsTrustedProxies, currentConf.MetricsTrustedProxies) ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
closeAuthManager ||
|
|
closeLogger
|
|
|
|
closePPROF := newConf == nil ||
|
|
newConf.PPROF != currentConf.PPROF ||
|
|
newConf.PPROFAddress != currentConf.PPROFAddress ||
|
|
newConf.PPROFEncryption != currentConf.PPROFEncryption ||
|
|
newConf.PPROFServerKey != currentConf.PPROFServerKey ||
|
|
newConf.PPROFServerCert != currentConf.PPROFServerCert ||
|
|
!slices.Equal(newConf.PPROFAllowOrigins, currentConf.PPROFAllowOrigins) ||
|
|
!reflect.DeepEqual(newConf.PPROFTrustedProxies, currentConf.PPROFTrustedProxies) ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
closeAuthManager ||
|
|
closeLogger
|
|
|
|
closeRecorderCleaner := newConf == nil ||
|
|
atLeastOneRecordDeleteAfter(newConf.Paths) != atLeastOneRecordDeleteAfter(currentConf.Paths) ||
|
|
closeLogger
|
|
if !closeRecorderCleaner && p.recordCleaner != nil && !reflect.DeepEqual(newConf.Paths, currentConf.Paths) {
|
|
p.recordCleaner.ReloadPathConfs(newConf.Paths)
|
|
}
|
|
|
|
closePlaybackServer := newConf == nil ||
|
|
newConf.Playback != currentConf.Playback ||
|
|
newConf.PlaybackAddress != currentConf.PlaybackAddress ||
|
|
newConf.PlaybackEncryption != currentConf.PlaybackEncryption ||
|
|
newConf.PlaybackServerKey != currentConf.PlaybackServerKey ||
|
|
newConf.PlaybackServerCert != currentConf.PlaybackServerCert ||
|
|
!slices.Equal(newConf.PlaybackAllowOrigins, currentConf.PlaybackAllowOrigins) ||
|
|
!reflect.DeepEqual(newConf.PlaybackTrustedProxies, currentConf.PlaybackTrustedProxies) ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
closeAuthManager ||
|
|
closeLogger
|
|
if !closePlaybackServer && p.playbackServer != nil && !reflect.DeepEqual(newConf.Paths, currentConf.Paths) {
|
|
p.playbackServer.ReloadPathConfs(newConf.Paths)
|
|
}
|
|
|
|
closePathManager := newConf == nil ||
|
|
newConf.LogLevel != currentConf.LogLevel ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
newConf.RTSPAddress != currentConf.RTSPAddress ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.WriteQueueSize != currentConf.WriteQueueSize ||
|
|
newConf.UDPReadBufferSize != currentConf.UDPReadBufferSize ||
|
|
newConf.UDPMaxPayloadSize != currentConf.UDPMaxPayloadSize ||
|
|
newConf.RTSPEncryption != currentConf.RTSPEncryption ||
|
|
closeMetrics ||
|
|
closeAuthManager ||
|
|
closeLogger
|
|
if !closePathManager && !reflect.DeepEqual(newConf.Paths, currentConf.Paths) {
|
|
p.pathManager.ReloadPathConfs(newConf.Paths)
|
|
}
|
|
|
|
closeRTSPServer := newConf == nil ||
|
|
newConf.RTSP != currentConf.RTSP ||
|
|
newConf.RTSPEncryption != currentConf.RTSPEncryption ||
|
|
newConf.RTSPAddress != currentConf.RTSPAddress ||
|
|
!reflect.DeepEqual(newConf.RTSPAuthMethods, currentConf.RTSPAuthMethods) ||
|
|
newConf.RTSPUDPReadBufferSize != currentConf.RTSPUDPReadBufferSize ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
newConf.UDPReadBufferSize != currentConf.UDPReadBufferSize ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.WriteQueueSize != currentConf.WriteQueueSize ||
|
|
newConf.RTPAddress != currentConf.RTPAddress ||
|
|
newConf.RTCPAddress != currentConf.RTCPAddress ||
|
|
newConf.MulticastIPRange != currentConf.MulticastIPRange ||
|
|
newConf.MulticastRTPPort != currentConf.MulticastRTPPort ||
|
|
newConf.MulticastRTCPPort != currentConf.MulticastRTCPPort ||
|
|
!reflect.DeepEqual(newConf.RTSPTransports, currentConf.RTSPTransports) ||
|
|
!reflect.DeepEqual(newConf.RTSPTrustedProxies, currentConf.RTSPTrustedProxies) ||
|
|
newConf.RunOnConnect != currentConf.RunOnConnect ||
|
|
newConf.RunOnConnectRestart != currentConf.RunOnConnectRestart ||
|
|
newConf.RunOnDisconnect != currentConf.RunOnDisconnect ||
|
|
closeMetrics ||
|
|
closePathManager ||
|
|
closeLogger
|
|
|
|
closeRTSPSServer := newConf == nil ||
|
|
newConf.RTSP != currentConf.RTSP ||
|
|
newConf.RTSPEncryption != currentConf.RTSPEncryption ||
|
|
newConf.RTSPSAddress != currentConf.RTSPSAddress ||
|
|
!reflect.DeepEqual(newConf.RTSPAuthMethods, currentConf.RTSPAuthMethods) ||
|
|
newConf.RTSPUDPReadBufferSize != currentConf.RTSPUDPReadBufferSize ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
newConf.UDPReadBufferSize != currentConf.UDPReadBufferSize ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.WriteQueueSize != currentConf.WriteQueueSize ||
|
|
newConf.RTSPServerCert != currentConf.RTSPServerCert ||
|
|
newConf.RTSPServerKey != currentConf.RTSPServerKey ||
|
|
newConf.RTSPAddress != currentConf.RTSPAddress ||
|
|
!reflect.DeepEqual(newConf.RTSPTransports, currentConf.RTSPTransports) ||
|
|
!reflect.DeepEqual(newConf.RTSPTrustedProxies, currentConf.RTSPTrustedProxies) ||
|
|
newConf.RunOnConnect != currentConf.RunOnConnect ||
|
|
newConf.RunOnConnectRestart != currentConf.RunOnConnectRestart ||
|
|
newConf.RunOnDisconnect != currentConf.RunOnDisconnect ||
|
|
closeMetrics ||
|
|
closePathManager ||
|
|
closeLogger
|
|
|
|
closeRTMPServer := newConf == nil ||
|
|
newConf.RTMP != currentConf.RTMP ||
|
|
newConf.RTMPEncryption != currentConf.RTMPEncryption ||
|
|
newConf.RTMPAddress != currentConf.RTMPAddress ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.RTSPAddress != currentConf.RTSPAddress ||
|
|
!reflect.DeepEqual(newConf.RTMPTrustedProxies, currentConf.RTMPTrustedProxies) ||
|
|
newConf.RunOnConnect != currentConf.RunOnConnect ||
|
|
newConf.RunOnConnectRestart != currentConf.RunOnConnectRestart ||
|
|
newConf.RunOnDisconnect != currentConf.RunOnDisconnect ||
|
|
closeMetrics ||
|
|
closePathManager ||
|
|
closeLogger
|
|
|
|
closeRTMPSServer := newConf == nil ||
|
|
newConf.RTMP != currentConf.RTMP ||
|
|
newConf.RTMPEncryption != currentConf.RTMPEncryption ||
|
|
newConf.RTMPSAddress != currentConf.RTMPSAddress ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.RTMPServerCert != currentConf.RTMPServerCert ||
|
|
newConf.RTMPServerKey != currentConf.RTMPServerKey ||
|
|
newConf.RTSPAddress != currentConf.RTSPAddress ||
|
|
!reflect.DeepEqual(newConf.RTMPTrustedProxies, currentConf.RTMPTrustedProxies) ||
|
|
newConf.RunOnConnect != currentConf.RunOnConnect ||
|
|
newConf.RunOnConnectRestart != currentConf.RunOnConnectRestart ||
|
|
newConf.RunOnDisconnect != currentConf.RunOnDisconnect ||
|
|
closeMetrics ||
|
|
closePathManager ||
|
|
closeLogger
|
|
|
|
closeHLSServer := newConf == nil ||
|
|
newConf.HLS != currentConf.HLS ||
|
|
newConf.HLSAddress != currentConf.HLSAddress ||
|
|
newConf.HLSEncryption != currentConf.HLSEncryption ||
|
|
newConf.HLSServerKey != currentConf.HLSServerKey ||
|
|
newConf.HLSServerCert != currentConf.HLSServerCert ||
|
|
!slices.Equal(newConf.HLSAllowOrigins, currentConf.HLSAllowOrigins) ||
|
|
!reflect.DeepEqual(newConf.HLSTrustedProxies, currentConf.HLSTrustedProxies) ||
|
|
newConf.HLSAlwaysRemux != currentConf.HLSAlwaysRemux ||
|
|
newConf.HLSVariant != currentConf.HLSVariant ||
|
|
newConf.HLSSegmentCount != currentConf.HLSSegmentCount ||
|
|
newConf.HLSSegmentDuration != currentConf.HLSSegmentDuration ||
|
|
newConf.HLSPartDuration != currentConf.HLSPartDuration ||
|
|
newConf.HLSSegmentMaxSize != currentConf.HLSSegmentMaxSize ||
|
|
newConf.HLSDirectory != currentConf.HLSDirectory ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.HLSMuxerCloseAfter != currentConf.HLSMuxerCloseAfter ||
|
|
newConf.HLSCDNSecret != currentConf.HLSCDNSecret ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
closePathManager ||
|
|
closeMetrics ||
|
|
closeLogger
|
|
|
|
closeWebRTCServer := newConf == nil ||
|
|
newConf.WebRTC != currentConf.WebRTC ||
|
|
newConf.WebRTCAddress != currentConf.WebRTCAddress ||
|
|
newConf.WebRTCEncryption != currentConf.WebRTCEncryption ||
|
|
newConf.WebRTCServerKey != currentConf.WebRTCServerKey ||
|
|
newConf.WebRTCServerCert != currentConf.WebRTCServerCert ||
|
|
!slices.Equal(newConf.WebRTCAllowOrigins, currentConf.WebRTCAllowOrigins) ||
|
|
!reflect.DeepEqual(newConf.WebRTCTrustedProxies, currentConf.WebRTCTrustedProxies) ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.UDPReadBufferSize != currentConf.UDPReadBufferSize ||
|
|
newConf.WebRTCLocalUDPAddress != currentConf.WebRTCLocalUDPAddress ||
|
|
newConf.WebRTCLocalTCPAddress != currentConf.WebRTCLocalTCPAddress ||
|
|
newConf.WebRTCIPsFromInterfaces != currentConf.WebRTCIPsFromInterfaces ||
|
|
!reflect.DeepEqual(newConf.WebRTCIPsFromInterfacesList, currentConf.WebRTCIPsFromInterfacesList) ||
|
|
!reflect.DeepEqual(newConf.WebRTCAdditionalHosts, currentConf.WebRTCAdditionalHosts) ||
|
|
!reflect.DeepEqual(newConf.WebRTCICEServers2, currentConf.WebRTCICEServers2) ||
|
|
newConf.WebRTCSTUNGatherTimeout != currentConf.WebRTCSTUNGatherTimeout ||
|
|
newConf.WebRTCHandshakeTimeout != currentConf.WebRTCHandshakeTimeout ||
|
|
newConf.WebRTCTrackGatherTimeout != currentConf.WebRTCTrackGatherTimeout ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
closeMetrics ||
|
|
closePathManager ||
|
|
closeLogger
|
|
|
|
closeSRTServer := newConf == nil ||
|
|
newConf.SRT != currentConf.SRT ||
|
|
newConf.SRTAddress != currentConf.SRTAddress ||
|
|
newConf.RTSPAddress != currentConf.RTSPAddress ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.UDPMaxPayloadSize != currentConf.UDPMaxPayloadSize ||
|
|
newConf.RunOnConnect != currentConf.RunOnConnect ||
|
|
newConf.RunOnConnectRestart != currentConf.RunOnConnectRestart ||
|
|
newConf.RunOnDisconnect != currentConf.RunOnDisconnect ||
|
|
closeMetrics ||
|
|
closePathManager ||
|
|
closeLogger
|
|
|
|
closeMoQServer := newConf == nil ||
|
|
newConf.MoQ != currentConf.MoQ ||
|
|
newConf.MoQHTTP2Address != currentConf.MoQHTTP2Address ||
|
|
newConf.MoQHTTP3Address != currentConf.MoQHTTP3Address ||
|
|
newConf.MoQQUICAddress != currentConf.MoQQUICAddress ||
|
|
newConf.MoQServerKey != currentConf.MoQServerKey ||
|
|
newConf.MoQServerCert != currentConf.MoQServerCert ||
|
|
!slices.Equal(newConf.MoQAllowOrigins, currentConf.MoQAllowOrigins) ||
|
|
!reflect.DeepEqual(newConf.MoQTrustedProxies, currentConf.MoQTrustedProxies) ||
|
|
newConf.UDPReadBufferSize != currentConf.UDPReadBufferSize ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
closeMetrics ||
|
|
closePathManager ||
|
|
closeLogger
|
|
|
|
closeAPI := newConf == nil ||
|
|
newConf.API != currentConf.API ||
|
|
newConf.APIAddress != currentConf.APIAddress ||
|
|
newConf.APIEncryption != currentConf.APIEncryption ||
|
|
newConf.APIServerKey != currentConf.APIServerKey ||
|
|
newConf.APIServerCert != currentConf.APIServerCert ||
|
|
!slices.Equal(newConf.APIAllowOrigins, currentConf.APIAllowOrigins) ||
|
|
!reflect.DeepEqual(newConf.APITrustedProxies, currentConf.APITrustedProxies) ||
|
|
newConf.ReadTimeout != currentConf.ReadTimeout ||
|
|
newConf.WriteTimeout != currentConf.WriteTimeout ||
|
|
newConf.DumpPackets != currentConf.DumpPackets ||
|
|
closeAuthManager ||
|
|
closePathManager ||
|
|
closeRTSPServer ||
|
|
closeRTSPSServer ||
|
|
closeRTMPServer ||
|
|
closeRTMPSServer ||
|
|
closeHLSServer ||
|
|
closeWebRTCServer ||
|
|
closeSRTServer ||
|
|
closeMoQServer ||
|
|
closeLogger
|
|
|
|
if newConf == nil && p.confWatcher != nil {
|
|
p.confWatcher.Close()
|
|
p.confWatcher = nil
|
|
}
|
|
|
|
if p.api != nil {
|
|
if closeAPI {
|
|
p.api.Close()
|
|
p.api = nil
|
|
}
|
|
}
|
|
|
|
if closeSRTServer && p.srtServer != nil {
|
|
p.srtServer.Close()
|
|
p.srtServer = nil
|
|
}
|
|
|
|
if closeMoQServer && p.moqServer != nil {
|
|
p.moqServer.Close()
|
|
p.moqServer = nil
|
|
}
|
|
|
|
if closeWebRTCServer && p.webRTCServer != nil {
|
|
p.webRTCServer.Close()
|
|
p.webRTCServer = nil
|
|
}
|
|
|
|
if closeHLSServer && p.hlsServer != nil {
|
|
p.hlsServer.Close()
|
|
p.hlsServer = nil
|
|
}
|
|
|
|
if closeRTMPSServer && p.rtmpsServer != nil {
|
|
p.rtmpsServer.Close()
|
|
p.rtmpsServer = nil
|
|
}
|
|
|
|
if closeRTMPServer && p.rtmpServer != nil {
|
|
p.rtmpServer.Close()
|
|
p.rtmpServer = nil
|
|
}
|
|
|
|
if closeRTSPSServer && p.rtspsServer != nil {
|
|
p.rtspsServer.Close()
|
|
p.rtspsServer = nil
|
|
}
|
|
|
|
if closeRTSPServer && p.rtspServer != nil {
|
|
p.rtspServer.Close()
|
|
p.rtspServer = nil
|
|
}
|
|
|
|
if closePathManager && p.pathManager != nil {
|
|
p.pathManager.close()
|
|
p.pathManager = nil
|
|
}
|
|
|
|
if closePlaybackServer && p.playbackServer != nil {
|
|
p.playbackServer.Close()
|
|
p.playbackServer = nil
|
|
}
|
|
|
|
if closeRecorderCleaner && p.recordCleaner != nil {
|
|
p.recordCleaner.Close()
|
|
p.recordCleaner = nil
|
|
}
|
|
|
|
if closePPROF && p.pprof != nil {
|
|
p.pprof.Close()
|
|
p.pprof = nil
|
|
}
|
|
|
|
if closeMetrics && p.metrics != nil {
|
|
p.metrics.Close()
|
|
p.metrics = nil
|
|
}
|
|
|
|
if closeAuthManager && p.authManager != nil {
|
|
p.authManager = nil
|
|
}
|
|
|
|
if newConf == nil && p.externalCmdPool != nil {
|
|
p.Log(logger.Info, "waiting for running hooks")
|
|
p.externalCmdPool.Close()
|
|
}
|
|
|
|
if closeLogger && p.logger != nil {
|
|
if newConf == nil {
|
|
p.logger.Close()
|
|
}
|
|
p.logger = nil
|
|
}
|
|
}
|
|
|
|
func (p *Core) reloadConf(newConf *conf.Conf) error {
|
|
oldLogger := p.logger
|
|
|
|
p.closeResources(newConf)
|
|
|
|
p.conf.Store(newConf)
|
|
|
|
err := p.createResources(false)
|
|
if err != nil {
|
|
p.logger = oldLogger
|
|
return err
|
|
}
|
|
|
|
if p.logger != oldLogger {
|
|
oldLogger.Close()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Core) apiConfigSnapshot() *conf.Conf {
|
|
return p.conf.Load()
|
|
}
|
|
|
|
func (p *Core) doAPIConfigGlobalPatch(in conf.OptionalGlobal) (*conf.Conf, error) {
|
|
newConf := p.conf.Load().Clone()
|
|
newConf.PatchGlobal(&in)
|
|
|
|
if err := newConf.Validate(nil); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.Log(logger.Info, "reloading configuration (API request)")
|
|
return newConf, nil
|
|
}
|
|
|
|
func (p *Core) doAPIConfigPathDefaultsPatch(in conf.OptionalPath) (*conf.Conf, error) {
|
|
newConf := p.conf.Load().Clone()
|
|
newConf.PatchPathDefaults(&in)
|
|
|
|
if err := newConf.Validate(nil); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.Log(logger.Info, "reloading configuration (API request)")
|
|
return newConf, nil
|
|
}
|
|
|
|
func (p *Core) doAPIConfigPathAdd(name string, in conf.OptionalPath) (*conf.Conf, error) {
|
|
newConf := p.conf.Load().Clone()
|
|
|
|
if err := newConf.AddPath(name, &in); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := newConf.Validate(nil); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.Log(logger.Info, "reloading configuration (API request)")
|
|
return newConf, nil
|
|
}
|
|
|
|
func (p *Core) doAPIConfigPathPatch(name string, in conf.OptionalPath) (*conf.Conf, error) {
|
|
newConf := p.conf.Load().Clone()
|
|
|
|
if err := newConf.PatchPath(name, &in); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := newConf.Validate(nil); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.Log(logger.Info, "reloading configuration (API request)")
|
|
return newConf, nil
|
|
}
|
|
|
|
func (p *Core) doAPIConfigPathReplace(name string, in conf.OptionalPath) (*conf.Conf, error) {
|
|
newConf := p.conf.Load().Clone()
|
|
|
|
if err := newConf.ReplacePath(name, &in); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := newConf.Validate(nil); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.Log(logger.Info, "reloading configuration (API request)")
|
|
return newConf, nil
|
|
}
|
|
|
|
func (p *Core) doAPIConfigPathDelete(name string) (*conf.Conf, error) {
|
|
newConf := p.conf.Load().Clone()
|
|
|
|
if err := newConf.RemovePath(name); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := newConf.Validate(nil); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.Log(logger.Info, "reloading configuration (API request)")
|
|
return newConf, nil
|
|
}
|
|
|
|
// APIConfigSnapshot implements apiParent.
|
|
func (p *Core) APIConfigSnapshot() *conf.Conf {
|
|
return p.apiConfigSnapshot()
|
|
}
|
|
|
|
// APIConfigGlobalPatch implements apiParent.
|
|
func (p *Core) APIConfigGlobalPatch(in conf.OptionalGlobal) error {
|
|
res := make(chan error)
|
|
select {
|
|
case p.chAPIConfigGlobalPatch <- configGlobalPatchReq{conf: in, res: res}:
|
|
return <-res
|
|
case <-p.ctx.Done():
|
|
return fmt.Errorf("terminated")
|
|
}
|
|
}
|
|
|
|
// APIConfigPathDefaultsPatch implements apiParent.
|
|
func (p *Core) APIConfigPathDefaultsPatch(in conf.OptionalPath) error {
|
|
res := make(chan error)
|
|
select {
|
|
case p.chAPIConfigPathDefaultsPatch <- configPathDefaultsPatchReq{conf: in, res: res}:
|
|
return <-res
|
|
case <-p.ctx.Done():
|
|
return fmt.Errorf("terminated")
|
|
}
|
|
}
|
|
|
|
// APIConfigPathsAdd implements apiParent.
|
|
func (p *Core) APIConfigPathsAdd(name string, in conf.OptionalPath) error {
|
|
res := make(chan error)
|
|
select {
|
|
case p.chAPIConfigPathAdd <- configPathAddReq{name: name, conf: in, res: res}:
|
|
return <-res
|
|
case <-p.ctx.Done():
|
|
return fmt.Errorf("terminated")
|
|
}
|
|
}
|
|
|
|
// APIConfigPathsPatch implements apiParent.
|
|
func (p *Core) APIConfigPathsPatch(name string, in conf.OptionalPath) error {
|
|
res := make(chan error)
|
|
select {
|
|
case p.chAPIConfigPathPatch <- configPathPatchReq{name: name, conf: in, res: res}:
|
|
return <-res
|
|
case <-p.ctx.Done():
|
|
return fmt.Errorf("terminated")
|
|
}
|
|
}
|
|
|
|
// APIConfigPathsReplace implements apiParent.
|
|
func (p *Core) APIConfigPathsReplace(name string, in conf.OptionalPath) error {
|
|
res := make(chan error)
|
|
select {
|
|
case p.chAPIConfigPathReplace <- configPathReplaceReq{name: name, conf: in, res: res}:
|
|
return <-res
|
|
case <-p.ctx.Done():
|
|
return fmt.Errorf("terminated")
|
|
}
|
|
}
|
|
|
|
// APIConfigPathsDelete implements apiParent.
|
|
func (p *Core) APIConfigPathsDelete(name string) error {
|
|
res := make(chan error)
|
|
select {
|
|
case p.chAPIConfigPathDelete <- configPathDeleteReq{name: name, res: res}:
|
|
return <-res
|
|
case <-p.ctx.Done():
|
|
return fmt.Errorf("terminated")
|
|
}
|
|
}
|