Skip to content

Commit

Permalink
add playback server
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed Jan 14, 2024
1 parent 933029f commit 2ce1bf9
Show file tree
Hide file tree
Showing 24 changed files with 984 additions and 287 deletions.
16 changes: 12 additions & 4 deletions apidocs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ components:
type: integer
externalAuthenticationURL:
type: string
api:
type: boolean
apiAddress:
type: string
metrics:
type: boolean
metricsAddress:
Expand All @@ -62,6 +58,18 @@ components:
runOnDisconnect:
type: string

# API
api:
type: boolean
apiAddress:
type: string

# Playback server
playback:
type: boolean
playbackAddress:
type: string

# RTSP server
rtsp:
type: boolean
Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ require (
code.cloudfoundry.org/bytefmt v0.0.0
github.com/abema/go-mp4 v1.1.1
github.com/alecthomas/kong v0.8.1
github.com/aler9/writerseeker v1.1.0
github.com/bluenviron/gohlslib v1.2.0
github.com/bluenviron/gohlslib v1.2.1-0.20240114214154-83fc88edbaad
github.com/bluenviron/gortsplib/v4 v4.6.4-0.20240108201647-63a81d0896f5
github.com/bluenviron/mediacommon v1.7.1
github.com/bluenviron/mediacommon v1.7.2-0.20240114212945-d286fa237f05
github.com/datarhei/gosrt v0.5.5
github.com/fsnotify/fsnotify v1.7.0
github.com/gin-gonic/gin v1.9.1
Expand Down
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,18 @@ github.com/aler9/sdp/v3 v3.0.0-20231022165400-33437e07f326 h1:HA7u47vkcxFiHtiOjm
github.com/aler9/sdp/v3 v3.0.0-20231022165400-33437e07f326/go.mod h1:I40uD/ZSmK2peI6AdJga5fd55d4bFK0oWOgLS9Q8sVc=
github.com/aler9/webrtc/v3 v3.0.0-20231112223655-e402ed2689c6 h1:wMd3D1mLghoYYh31STig8Kwm2qi8QyQKUy09qUUZrVw=
github.com/aler9/webrtc/v3 v3.0.0-20231112223655-e402ed2689c6/go.mod h1:1CaT2fcZzZ6VZA+O1i9yK2DU4EOcXVvSbWG9pr5jefs=
github.com/aler9/writerseeker v1.1.0 h1:t+Sm3tjp8scNlqyoa8obpeqwciMNOvdvsxjxEb3Sx3g=
github.com/aler9/writerseeker v1.1.0/go.mod h1:QNCcjSKnLsYoTfMmXkEEfgbz6nNXWxKSaBY+hGJGWDA=
github.com/asticode/go-astikit v0.30.0 h1:DkBkRQRIxYcknlaU7W7ksNfn4gMFsB0tqMJflxkRsZA=
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
github.com/asticode/go-astits v1.13.0 h1:XOgkaadfZODnyZRR5Y0/DWkA9vrkLLPLeeOvDwfKZ1c=
github.com/asticode/go-astits v1.13.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYhJeJ2aZxADI2tGADS15AzIF8MQ8XAhT4=
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
github.com/bluenviron/gohlslib v1.2.0 h1:Hrx2/n/AcmKKIV+MjZLKs5kmW+O7xCdUSPJQoS39JKw=
github.com/bluenviron/gohlslib v1.2.0/go.mod h1:kG/Sjebsxnf5asMGaGcQ0aSvtFGNChJPgctds2wDHOI=
github.com/bluenviron/gohlslib v1.2.1-0.20240114214154-83fc88edbaad h1:R9Lqf0A2/3TTB4casoU1LC+HRLmsVxNYUTmnbbD8WAE=
github.com/bluenviron/gohlslib v1.2.1-0.20240114214154-83fc88edbaad/go.mod h1:k94WhiVkgJl45Q1WkLw8/GG2AJ1+VU9c/3i4f41xMq8=
github.com/bluenviron/gortsplib/v4 v4.6.4-0.20240108201647-63a81d0896f5 h1:DU63YvVivfcIq7jcA1KCwi9YO+jEVglYNJCIrOLj8QE=
github.com/bluenviron/gortsplib/v4 v4.6.4-0.20240108201647-63a81d0896f5/go.mod h1:UqdkRR5YvKHP/wHEQQySJFKJm6tIZcftdP7cNszIZ1g=
github.com/bluenviron/mediacommon v1.7.1 h1:7Lm2b8M9gUk3Ben1w+OPwadAeYseIBscwgdiL+aLtfw=
github.com/bluenviron/mediacommon v1.7.1/go.mod h1:Ij/kE1LEucSjryNBVTyPL/gBI0d6/Css3f5PyrM957w=
github.com/bluenviron/mediacommon v1.7.2-0.20240114212945-d286fa237f05 h1:GWkgbRYy8J8+pI4vmU1J9JPGncPvG3mCT1twbxbX4eM=
github.com/bluenviron/mediacommon v1.7.2-0.20240114212945-d286fa237f05/go.mod h1:TQIxPCXBfrWhagGeB9ajiUy6GlImTC8s1x2DOMVIrNw=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
Expand Down
3 changes: 1 addition & 2 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,11 @@ func (a *API) Log(level logger.Level, format string, args ...interface{}) {
a.Parent.Log(level, "[API] "+format, args...)
}

// error coming from something the user inserted into the request.
func (a *API) writeError(ctx *gin.Context, status int, err error) {
// show error in logs
a.Log(logger.Error, err.Error())

// send error in response
// add error to response

Check warning on line 271 in internal/api/api.go

View check run for this annotation

Codecov / codecov/patch

internal/api/api.go#L271

Added line #L271 was not covered by tests
ctx.JSON(status, &defs.APIError{
Error: err.Error(),
})
Expand Down
25 changes: 18 additions & 7 deletions internal/conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ type Conf struct {
WriteQueueSize int `json:"writeQueueSize"`
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"`
Expand All @@ -100,6 +98,14 @@ type Conf struct {
RunOnConnectRestart bool `json:"runOnConnectRestart"`
RunOnDisconnect string `json:"runOnDisconnect"`

// API
API bool `json:"api"`
APIAddress string `json:"apiAddress"`

// Playback
Playback bool `json:"playback"`
PlaybackAddress string `json:"playbackAddress"`

// RTSP server
RTSP bool `json:"rtsp"`
RTSPDisable *bool `json:"rtspDisable,omitempty"` // deprecated
Expand Down Expand Up @@ -191,11 +197,16 @@ func (conf *Conf) setDefaults() {
conf.WriteTimeout = 10 * StringDuration(time.Second)
conf.WriteQueueSize = 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
// API
conf.APIAddress = "127.0.0.1:9997"

// Playback server
conf.PlaybackAddress = ":9996"

// RTSP server
conf.RTSP = true
conf.Protocols = Protocols{
Protocol(gortsplib.TransportUDP): {},
Expand All @@ -213,7 +224,7 @@ func (conf *Conf) setDefaults() {
conf.ServerCert = "server.crt"
conf.AuthMethods = AuthMethods{headers.AuthBasic}

// RTMP
// RTMP server
conf.RTMP = true
conf.RTMPAddress = ":1935"
conf.RTMPSAddress = ":1936"
Expand All @@ -232,7 +243,7 @@ func (conf *Conf) setDefaults() {
conf.HLSSegmentMaxSize = 50 * 1024 * 1024
conf.HLSAllowOrigin = "*"

// WebRTC
// WebRTC server
conf.WebRTC = true
conf.WebRTCAddress = ":8889"
conf.WebRTCServerKey = "server.key"
Expand All @@ -244,7 +255,7 @@ func (conf *Conf) setDefaults() {
conf.WebRTCAdditionalHosts = []string{}
conf.WebRTCICEServers2 = []WebRTCICEServer{}

// SRT
// SRT server
conf.SRT = true
conf.SRTAddress = ":8890"

Expand Down
42 changes: 38 additions & 4 deletions internal/conf/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import (

var rePathName = regexp.MustCompile(`^[0-9a-zA-Z_\-/\.~]+$`)

// IsValidPathName checks if a path name is valid.
func IsValidPathName(name string) error {
func isValidPathName(name string) error {
if name == "" {
return fmt.Errorf("cannot be empty")
}
Expand Down Expand Up @@ -47,6 +46,41 @@ func srtCheckPassphrase(passphrase string) error {
}
}

// FindPathConf returns the configuration corresponding to the given path name.
func FindPathConf(pathConfs map[string]*Path, name string) (string, *Path, []string, error) {
err := isValidPathName(name)
if err != nil {
return "", nil, nil, fmt.Errorf("invalid path name: %w (%s)", err, name)
}

Check warning on line 54 in internal/conf/path.go

View check run for this annotation

Codecov / codecov/patch

internal/conf/path.go#L50-L54

Added lines #L50 - L54 were not covered by tests

// normal path
if pathConf, ok := pathConfs[name]; ok {
return name, pathConf, nil, nil
}

Check warning on line 59 in internal/conf/path.go

View check run for this annotation

Codecov / codecov/patch

internal/conf/path.go#L57-L59

Added lines #L57 - L59 were not covered by tests

// regular expression-based path
for pathConfName, pathConf := range pathConfs {
if pathConf.Regexp != nil && pathConfName != "all" && pathConfName != "all_others" {
m := pathConf.Regexp.FindStringSubmatch(name)
if m != nil {
return pathConfName, pathConf, m, nil
}

Check warning on line 67 in internal/conf/path.go

View check run for this annotation

Codecov / codecov/patch

internal/conf/path.go#L62-L67

Added lines #L62 - L67 were not covered by tests
}
}

// all_others
for pathConfName, pathConf := range pathConfs {
if pathConfName == "all" || pathConfName == "all_others" {
m := pathConf.Regexp.FindStringSubmatch(name)
if m != nil {
return pathConfName, pathConf, m, nil
}

Check warning on line 77 in internal/conf/path.go

View check run for this annotation

Codecov / codecov/patch

internal/conf/path.go#L72-L77

Added lines #L72 - L77 were not covered by tests
}
}

return "", nil, nil, fmt.Errorf("path '%s' is not configured", name)

Check warning on line 81 in internal/conf/path.go

View check run for this annotation

Codecov / codecov/patch

internal/conf/path.go#L81

Added line #L81 was not covered by tests
}

// Path is a path configuration.
type Path struct {
Regexp *regexp.Regexp `json:"-"` // filled by Check()
Expand Down Expand Up @@ -220,7 +254,7 @@ func (pconf *Path) check(conf *Conf, name string) error {
pconf.Regexp = regexp.MustCompile("^.*$")

case name == "" || name[0] != '~': // normal path
err := IsValidPathName(name)
err := isValidPathName(name)
if err != nil {
return fmt.Errorf("invalid path name '%s': %w", name, err)
}
Expand Down Expand Up @@ -325,7 +359,7 @@ func (pconf *Path) check(conf *Conf, name string) error {
}
if pconf.Fallback != "" {
if strings.HasPrefix(pconf.Fallback, "/") {
err := IsValidPathName(pconf.Fallback[1:])
err := isValidPathName(pconf.Fallback[1:])

Check warning on line 362 in internal/conf/path.go

View check run for this annotation

Codecov / codecov/patch

internal/conf/path.go#L362

Added line #L362 was not covered by tests
if err != nil {
return fmt.Errorf("'%s': %w", pconf.Fallback, err)
}
Expand Down
65 changes: 48 additions & 17 deletions internal/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"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/record"
"github.com/bluenviron/mediamtx/internal/rlimit"
Expand All @@ -48,7 +49,7 @@ func gatherCleanerEntries(paths map[string]*conf.Path) []record.CleanerEntry {
for _, pa := range paths {
if pa.Record && pa.RecordDeleteAfter != 0 {
entry := record.CleanerEntry{
PathFormat: pa.RecordPath,
Path: pa.RecordPath,
Format: pa.RecordFormat,
DeleteAfter: time.Duration(pa.RecordDeleteAfter),
}
Expand All @@ -65,8 +66,8 @@ func gatherCleanerEntries(paths map[string]*conf.Path) []record.CleanerEntry {
}

sort.Slice(out2, func(i, j int) bool {
if out2[i].PathFormat != out2[j].PathFormat {
return out2[i].PathFormat < out2[j].PathFormat
if out2[i].Path != out2[j].Path {
return out2[i].Path < out2[j].Path

Check warning on line 70 in internal/core/core.go

View check run for this annotation

Codecov / codecov/patch

internal/core/core.go#L69-L70

Added lines #L69 - L70 were not covered by tests
}
return out2[i].DeleteAfter < out2[j].DeleteAfter
})
Expand All @@ -90,6 +91,7 @@ type Core struct {
metrics *metrics.Metrics
pprof *pprof.PPROF
recordCleaner *record.Cleaner
playbackServer *playback.Server
pathManager *pathManager
rtspServer *rtsp.Server
rtspsServer *rtsp.Server
Expand Down Expand Up @@ -312,20 +314,35 @@ func (p *Core) createResources(initial bool) error {
p.recordCleaner.Initialize()
}

if p.conf.Playback &&
p.playbackServer == nil {
p.playbackServer = &playback.Server{
Address: p.conf.PlaybackAddress,
ReadTimeout: p.conf.ReadTimeout,
PathConfs: p.conf.Paths,
Parent: p,
}
err := p.playbackServer.Initialize()
if err != nil {
return err
}

Check warning on line 328 in internal/core/core.go

View check run for this annotation

Codecov / codecov/patch

internal/core/core.go#L319-L328

Added lines #L319 - L328 were not covered by tests
}

if p.pathManager == nil {
p.pathManager = newPathManager(
p.conf.LogLevel,
p.conf.ExternalAuthenticationURL,
p.conf.RTSPAddress,
p.conf.AuthMethods,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
p.conf.UDPMaxPayloadSize,
p.conf.Paths,
p.externalCmdPool,
p,
)
p.pathManager = &pathManager{
logLevel: p.conf.LogLevel,
externalAuthenticationURL: p.conf.ExternalAuthenticationURL,
rtspAddress: p.conf.RTSPAddress,
authMethods: p.conf.AuthMethods,
readTimeout: p.conf.ReadTimeout,
writeTimeout: p.conf.WriteTimeout,
writeQueueSize: p.conf.WriteQueueSize,
udpMaxPayloadSize: p.conf.UDPMaxPayloadSize,
pathConfs: p.conf.Paths,
externalCmdPool: p.externalCmdPool,
parent: p,
}
p.pathManager.initialize()

if p.metrics != nil {
p.metrics.SetPathManager(p.pathManager)
Expand Down Expand Up @@ -619,6 +636,15 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
!reflect.DeepEqual(gatherCleanerEntries(newConf.Paths), gatherCleanerEntries(p.conf.Paths)) ||
closeLogger

closePlaybackServer := newConf == nil ||
newConf.Playback != p.conf.Playback ||
newConf.PlaybackAddress != p.conf.PlaybackAddress ||
newConf.ReadTimeout != p.conf.ReadTimeout ||
closeLogger
if !closePlaybackServer && p.playbackServer != nil && !reflect.DeepEqual(newConf.Paths, p.conf.Paths) {
p.playbackServer.ReloadPathConfs(newConf.Paths)
}

Check warning on line 646 in internal/core/core.go

View check run for this annotation

Codecov / codecov/patch

internal/core/core.go#L645-L646

Added lines #L645 - L646 were not covered by tests

closePathManager := newConf == nil ||
newConf.LogLevel != p.conf.LogLevel ||
newConf.ExternalAuthenticationURL != p.conf.ExternalAuthenticationURL ||
Expand All @@ -631,7 +657,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
closeMetrics ||
closeLogger
if !closePathManager && !reflect.DeepEqual(newConf.Paths, p.conf.Paths) {
p.pathManager.ReloadConf(newConf.Paths)
p.pathManager.ReloadPathConfs(newConf.Paths)
}

closeRTSPServer := newConf == nil ||
Expand Down Expand Up @@ -865,6 +891,11 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
p.pathManager = nil
}

if closePlaybackServer && p.playbackServer != nil {
p.playbackServer.Close()
p.playbackServer = nil
}

Check warning on line 897 in internal/core/core.go

View check run for this annotation

Codecov / codecov/patch

internal/core/core.go#L895-L897

Added lines #L895 - L897 were not covered by tests

if closeRecorderCleaner && p.recordCleaner != nil {
p.recordCleaner.Close()
p.recordCleaner = nil
Expand Down
Loading

0 comments on commit 2ce1bf9

Please sign in to comment.