Browse Source

record: rename segmentPath into path (#2777)

pull/2782/head
Alessandro Ros 2 years ago committed by GitHub
parent
commit
20123fd5b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      internal/core/core.go
  2. 22
      internal/core/path.go
  3. 2
      internal/record/agent.go
  4. 14
      internal/record/agent_instance.go
  5. 30
      internal/record/agent_test.go
  6. 22
      internal/record/cleaner.go
  7. 6
      internal/record/cleaner_test.go
  8. 2
      internal/record/format_fmp4_part.go
  9. 2
      internal/record/format_mpegts_segment.go
  10. 28
      internal/record/path.go
  11. 22
      internal/record/path_test.go

10
internal/core/core.go

@ -40,9 +40,9 @@ func gatherCleanerEntries(paths map[string]*conf.Path) []record.CleanerEntry {
for _, pa := range paths { for _, pa := range paths {
if pa.Record && pa.RecordDeleteAfter != 0 { if pa.Record && pa.RecordDeleteAfter != 0 {
entry := record.CleanerEntry{ entry := record.CleanerEntry{
SegmentPathFormat: pa.RecordPath, PathFormat: pa.RecordPath,
Format: pa.RecordFormat, Format: pa.RecordFormat,
DeleteAfter: time.Duration(pa.RecordDeleteAfter), DeleteAfter: time.Duration(pa.RecordDeleteAfter),
} }
out[entry] = struct{}{} out[entry] = struct{}{}
} }
@ -57,8 +57,8 @@ func gatherCleanerEntries(paths map[string]*conf.Path) []record.CleanerEntry {
} }
sort.Slice(out2, func(i, j int) bool { sort.Slice(out2, func(i, j int) bool {
if out2[i].SegmentPathFormat != out2[j].SegmentPathFormat { if out2[i].PathFormat != out2[j].PathFormat {
return out2[i].SegmentPathFormat < out2[j].SegmentPathFormat return out2[i].PathFormat < out2[j].PathFormat
} }
return out2[i].DeleteAfter < out2[j].DeleteAfter return out2[i].DeleteAfter < out2[j].DeleteAfter
}) })

22
internal/core/path.go

@ -897,17 +897,17 @@ func (pa *path) setNotReady() {
func (pa *path) startRecording() { func (pa *path) startRecording() {
pa.recordAgent = &record.Agent{ pa.recordAgent = &record.Agent{
WriteQueueSize: pa.writeQueueSize, WriteQueueSize: pa.writeQueueSize,
SegmentPathFormat: pa.conf.RecordPath, PathFormat: pa.conf.RecordPath,
Format: pa.conf.RecordFormat, Format: pa.conf.RecordFormat,
PartDuration: time.Duration(pa.conf.RecordPartDuration), PartDuration: time.Duration(pa.conf.RecordPartDuration),
SegmentDuration: time.Duration(pa.conf.RecordSegmentDuration), SegmentDuration: time.Duration(pa.conf.RecordSegmentDuration),
PathName: pa.name, PathName: pa.name,
Stream: pa.stream, Stream: pa.stream,
OnSegmentCreate: func(segmentPath string) { OnSegmentCreate: func(path string) {
if pa.conf.RunOnRecordSegmentCreate != "" { if pa.conf.RunOnRecordSegmentCreate != "" {
env := pa.externalCmdEnv() env := pa.externalCmdEnv()
env["MTX_SEGMENT_PATH"] = segmentPath env["MTX_SEGMENT_PATH"] = path
pa.Log(logger.Info, "runOnRecordSegmentCreate command launched") pa.Log(logger.Info, "runOnRecordSegmentCreate command launched")
externalcmd.NewCmd( externalcmd.NewCmd(
@ -918,10 +918,10 @@ func (pa *path) startRecording() {
nil) nil)
} }
}, },
OnSegmentComplete: func(segmentPath string) { OnSegmentComplete: func(path string) {
if pa.conf.RunOnRecordSegmentComplete != "" { if pa.conf.RunOnRecordSegmentComplete != "" {
env := pa.externalCmdEnv() env := pa.externalCmdEnv()
env["MTX_SEGMENT_PATH"] = segmentPath env["MTX_SEGMENT_PATH"] = path
pa.Log(logger.Info, "runOnRecordSegmentComplete command launched") pa.Log(logger.Info, "runOnRecordSegmentComplete command launched")
externalcmd.NewCmd( externalcmd.NewCmd(

2
internal/record/agent.go

@ -11,7 +11,7 @@ import (
// Agent writes recordings to disk. // Agent writes recordings to disk.
type Agent struct { type Agent struct {
WriteQueueSize int WriteQueueSize int
SegmentPathFormat string PathFormat string
Format conf.RecordFormat Format conf.RecordFormat
PartDuration time.Duration PartDuration time.Duration
SegmentDuration time.Duration SegmentDuration time.Duration

14
internal/record/agent_instance.go

@ -22,25 +22,25 @@ type sample struct {
type agentInstance struct { type agentInstance struct {
agent *Agent agent *Agent
segmentPathFormat string pathFormat string
writer *asyncwriter.Writer writer *asyncwriter.Writer
format format format format
terminate chan struct{} terminate chan struct{}
done chan struct{} done chan struct{}
} }
func (a *agentInstance) initialize() { func (a *agentInstance) initialize() {
a.segmentPathFormat = a.agent.SegmentPathFormat a.pathFormat = a.agent.PathFormat
a.segmentPathFormat = strings.ReplaceAll(a.segmentPathFormat, "%path", a.agent.PathName) a.pathFormat = strings.ReplaceAll(a.pathFormat, "%path", a.agent.PathName)
switch a.agent.Format { switch a.agent.Format {
case conf.RecordFormatMPEGTS: case conf.RecordFormatMPEGTS:
a.segmentPathFormat += ".ts" a.pathFormat += ".ts"
default: default:
a.segmentPathFormat += ".mp4" a.pathFormat += ".mp4"
} }
a.terminate = make(chan struct{}) a.terminate = make(chan struct{})

30
internal/record/agent_test.go

@ -156,13 +156,13 @@ func TestAgent(t *testing.T) {
} }
w := &Agent{ w := &Agent{
WriteQueueSize: 1024, WriteQueueSize: 1024,
SegmentPathFormat: recordPath, PathFormat: recordPath,
Format: f, Format: f,
PartDuration: 100 * time.Millisecond, PartDuration: 100 * time.Millisecond,
SegmentDuration: 1 * time.Second, SegmentDuration: 1 * time.Second,
PathName: "mypath", PathName: "mypath",
Stream: stream, Stream: stream,
OnSegmentCreate: func(fpath string) { OnSegmentCreate: func(fpath string) {
segCreated <- struct{}{} segCreated <- struct{}{}
}, },
@ -266,14 +266,14 @@ func TestAgentFMP4NegativeDTS(t *testing.T) {
recordPath := filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f") recordPath := filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f")
w := &Agent{ w := &Agent{
WriteQueueSize: 1024, WriteQueueSize: 1024,
SegmentPathFormat: recordPath, PathFormat: recordPath,
Format: conf.RecordFormatFMP4, Format: conf.RecordFormatFMP4,
PartDuration: 100 * time.Millisecond, PartDuration: 100 * time.Millisecond,
SegmentDuration: 1 * time.Second, SegmentDuration: 1 * time.Second,
PathName: "mypath", PathName: "mypath",
Stream: stream, Stream: stream,
Parent: &nilLogger{}, Parent: &nilLogger{},
} }
w.Initialize() w.Initialize()

22
internal/record/cleaner.go

@ -41,9 +41,9 @@ func commonPath(v string) string {
// CleanerEntry is a cleaner entry. // CleanerEntry is a cleaner entry.
type CleanerEntry struct { type CleanerEntry struct {
SegmentPathFormat string PathFormat string
Format conf.RecordFormat Format conf.RecordFormat
DeleteAfter time.Duration DeleteAfter time.Duration
} }
// Cleaner removes expired recording segments from disk. // Cleaner removes expired recording segments from disk.
@ -106,21 +106,21 @@ func (c *Cleaner) doRun() {
} }
func (c *Cleaner) doRunEntry(e *CleanerEntry) error { func (c *Cleaner) doRunEntry(e *CleanerEntry) error {
segmentPathFormat := e.SegmentPathFormat pathFormat := e.PathFormat
switch e.Format { switch e.Format {
case conf.RecordFormatMPEGTS: case conf.RecordFormatMPEGTS:
segmentPathFormat += ".ts" pathFormat += ".ts"
default: default:
segmentPathFormat += ".mp4" pathFormat += ".mp4"
} }
// we have to convert to absolute paths // we have to convert to absolute paths
// otherwise, commonPath and fpath inside Walk() won't have common elements // otherwise, commonPath and fpath inside Walk() won't have common elements
segmentPathFormat, _ = filepath.Abs(segmentPathFormat) pathFormat, _ = filepath.Abs(pathFormat)
commonPath := commonPath(segmentPathFormat) commonPath := commonPath(pathFormat)
now := timeNow() now := timeNow()
filepath.Walk(commonPath, func(fpath string, info fs.FileInfo, err error) error { //nolint:errcheck filepath.Walk(commonPath, func(fpath string, info fs.FileInfo, err error) error { //nolint:errcheck
@ -129,10 +129,10 @@ func (c *Cleaner) doRunEntry(e *CleanerEntry) error {
} }
if !info.IsDir() { if !info.IsDir() {
var pa segmentPath var pa path
ok := pa.decode(segmentPathFormat, fpath) ok := pa.decode(pathFormat, fpath)
if ok { if ok {
if now.Sub(pa.time) > e.DeleteAfter { if now.Sub(time.Time(pa)) > e.DeleteAfter {
c.Log(logger.Debug, "removing %s", fpath) c.Log(logger.Debug, "removing %s", fpath)
os.Remove(fpath) os.Remove(fpath)
} }

6
internal/record/cleaner_test.go

@ -33,9 +33,9 @@ func TestCleaner(t *testing.T) {
c := &Cleaner{ c := &Cleaner{
Entries: []CleanerEntry{{ Entries: []CleanerEntry{{
SegmentPathFormat: recordPath, PathFormat: recordPath,
Format: conf.RecordFormatFMP4, Format: conf.RecordFormatFMP4,
DeleteAfter: 10 * time.Second, DeleteAfter: 10 * time.Second,
}}, }},
Parent: nilLogger{}, Parent: nilLogger{},
} }

2
internal/record/format_fmp4_part.go

@ -65,7 +65,7 @@ func newFormatFMP4Part(
func (p *formatFMP4Part) close() error { func (p *formatFMP4Part) close() error {
if p.s.fi == nil { if p.s.fi == nil {
p.s.path = segmentPath{time: p.created}.encode(p.s.f.a.segmentPathFormat) p.s.path = path(p.created).encode(p.s.f.a.pathFormat)
p.s.f.a.agent.Log(logger.Debug, "creating segment %s", p.s.path) p.s.f.a.agent.Log(logger.Debug, "creating segment %s", p.s.path)
err := os.MkdirAll(filepath.Dir(p.s.path), 0o755) err := os.MkdirAll(filepath.Dir(p.s.path), 0o755)

2
internal/record/format_mpegts_segment.go

@ -51,7 +51,7 @@ func (s *formatMPEGTSSegment) close() error {
func (s *formatMPEGTSSegment) Write(p []byte) (int, error) { func (s *formatMPEGTSSegment) Write(p []byte) (int, error) {
if s.fi == nil { if s.fi == nil {
s.path = segmentPath{time: s.created}.encode(s.f.a.segmentPathFormat) s.path = path(s.created).encode(s.f.a.pathFormat)
s.f.a.agent.Log(logger.Debug, "creating segment %s", s.path) s.f.a.agent.Log(logger.Debug, "creating segment %s", s.path)
err := os.MkdirAll(filepath.Dir(s.path), 0o755) err := os.MkdirAll(filepath.Dir(s.path), 0o755)

28
internal/record/segment_path.go → internal/record/path.go

@ -21,11 +21,9 @@ func leadingZeros(v int, size int) string {
return out2 + out return out2 + out
} }
type segmentPath struct { type path time.Time
time time.Time
}
func (p *segmentPath) decode(format string, v string) bool { func (p *path) decode(format string, v string) bool {
re := format re := format
for _, ch := range []uint8{ for _, ch := range []uint8{
@ -143,22 +141,22 @@ func (p *segmentPath) decode(format string, v string) bool {
} }
if unixSec > 0 { if unixSec > 0 {
p.time = time.Unix(unixSec, 0) *p = path(time.Unix(unixSec, 0))
} else { } else {
p.time = time.Date(year, month, day, hour, minute, second, micros*1000, time.Local) *p = path(time.Date(year, month, day, hour, minute, second, micros*1000, time.Local))
} }
return true return true
} }
func (p segmentPath) encode(format string) string { func (p path) encode(format string) string {
format = strings.ReplaceAll(format, "%Y", strconv.FormatInt(int64(p.time.Year()), 10)) format = strings.ReplaceAll(format, "%Y", strconv.FormatInt(int64(time.Time(p).Year()), 10))
format = strings.ReplaceAll(format, "%m", leadingZeros(int(p.time.Month()), 2)) format = strings.ReplaceAll(format, "%m", leadingZeros(int(time.Time(p).Month()), 2))
format = strings.ReplaceAll(format, "%d", leadingZeros(p.time.Day(), 2)) format = strings.ReplaceAll(format, "%d", leadingZeros(time.Time(p).Day(), 2))
format = strings.ReplaceAll(format, "%H", leadingZeros(p.time.Hour(), 2)) format = strings.ReplaceAll(format, "%H", leadingZeros(time.Time(p).Hour(), 2))
format = strings.ReplaceAll(format, "%M", leadingZeros(p.time.Minute(), 2)) format = strings.ReplaceAll(format, "%M", leadingZeros(time.Time(p).Minute(), 2))
format = strings.ReplaceAll(format, "%S", leadingZeros(p.time.Second(), 2)) format = strings.ReplaceAll(format, "%S", leadingZeros(time.Time(p).Second(), 2))
format = strings.ReplaceAll(format, "%f", leadingZeros(p.time.Nanosecond()/1000, 6)) format = strings.ReplaceAll(format, "%f", leadingZeros(time.Time(p).Nanosecond()/1000, 6))
format = strings.ReplaceAll(format, "%s", strconv.FormatInt(p.time.Unix(), 10)) format = strings.ReplaceAll(format, "%s", strconv.FormatInt(time.Time(p).Unix(), 10))
return format return format
} }

22
internal/record/segment_path_test.go → internal/record/path_test.go

@ -7,34 +7,30 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var segmentPathCases = []struct { var pathCases = []struct {
name string name string
format string format string
dec segmentPath dec path
enc string enc string
}{ }{
{ {
"standard", "standard",
"%path/%Y-%m-%d_%H-%M-%S-%f.mp4", "%path/%Y-%m-%d_%H-%M-%S-%f.mp4",
segmentPath{ path(time.Date(2008, 11, 0o7, 11, 22, 4, 123456000, time.Local)),
time: time.Date(2008, 11, 0o7, 11, 22, 4, 123456000, time.Local),
},
"%path/2008-11-07_11-22-04-123456.mp4", "%path/2008-11-07_11-22-04-123456.mp4",
}, },
{ {
"unix seconds", "unix seconds",
"%path/%s.mp4", "%path/%s.mp4",
segmentPath{ path(time.Date(2021, 12, 2, 12, 15, 23, 0, time.UTC).Local()),
time: time.Date(2021, 12, 2, 12, 15, 23, 0, time.UTC).Local(),
},
"%path/1638447323.mp4", "%path/1638447323.mp4",
}, },
} }
func TestSegmentPathDecode(t *testing.T) { func TestPathDecode(t *testing.T) {
for _, ca := range segmentPathCases { for _, ca := range pathCases {
t.Run(ca.name, func(t *testing.T) { t.Run(ca.name, func(t *testing.T) {
var dec segmentPath var dec path
ok := dec.decode(ca.format, ca.enc) ok := dec.decode(ca.format, ca.enc)
require.Equal(t, true, ok) require.Equal(t, true, ok)
require.Equal(t, ca.dec, dec) require.Equal(t, ca.dec, dec)
@ -42,8 +38,8 @@ func TestSegmentPathDecode(t *testing.T) {
} }
} }
func TestSegmentPathEncode(t *testing.T) { func TestPathEncode(t *testing.T) {
for _, ca := range segmentPathCases { for _, ca := range pathCases {
t.Run(ca.name, func(t *testing.T) { t.Run(ca.name, func(t *testing.T) {
require.Equal(t, ca.enc, ca.dec.encode(ca.format)) require.Equal(t, ca.enc, ca.dec.encode(ca.format))
}) })
Loading…
Cancel
Save