diff --git a/internal/core/core.go b/internal/core/core.go index c7fcc3ee..242f73eb 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -40,9 +40,9 @@ func gatherCleanerEntries(paths map[string]*conf.Path) []record.CleanerEntry { for _, pa := range paths { if pa.Record && pa.RecordDeleteAfter != 0 { entry := record.CleanerEntry{ - SegmentPathFormat: pa.RecordPath, - Format: pa.RecordFormat, - DeleteAfter: time.Duration(pa.RecordDeleteAfter), + PathFormat: pa.RecordPath, + Format: pa.RecordFormat, + DeleteAfter: time.Duration(pa.RecordDeleteAfter), } out[entry] = struct{}{} } @@ -57,8 +57,8 @@ func gatherCleanerEntries(paths map[string]*conf.Path) []record.CleanerEntry { } sort.Slice(out2, func(i, j int) bool { - if out2[i].SegmentPathFormat != out2[j].SegmentPathFormat { - return out2[i].SegmentPathFormat < out2[j].SegmentPathFormat + if out2[i].PathFormat != out2[j].PathFormat { + return out2[i].PathFormat < out2[j].PathFormat } return out2[i].DeleteAfter < out2[j].DeleteAfter }) diff --git a/internal/core/path.go b/internal/core/path.go index 02b45fef..50deab66 100644 --- a/internal/core/path.go +++ b/internal/core/path.go @@ -897,17 +897,17 @@ func (pa *path) setNotReady() { func (pa *path) startRecording() { pa.recordAgent = &record.Agent{ - WriteQueueSize: pa.writeQueueSize, - SegmentPathFormat: pa.conf.RecordPath, - Format: pa.conf.RecordFormat, - PartDuration: time.Duration(pa.conf.RecordPartDuration), - SegmentDuration: time.Duration(pa.conf.RecordSegmentDuration), - PathName: pa.name, - Stream: pa.stream, - OnSegmentCreate: func(segmentPath string) { + WriteQueueSize: pa.writeQueueSize, + PathFormat: pa.conf.RecordPath, + Format: pa.conf.RecordFormat, + PartDuration: time.Duration(pa.conf.RecordPartDuration), + SegmentDuration: time.Duration(pa.conf.RecordSegmentDuration), + PathName: pa.name, + Stream: pa.stream, + OnSegmentCreate: func(path string) { if pa.conf.RunOnRecordSegmentCreate != "" { env := pa.externalCmdEnv() - env["MTX_SEGMENT_PATH"] = segmentPath + env["MTX_SEGMENT_PATH"] = path pa.Log(logger.Info, "runOnRecordSegmentCreate command launched") externalcmd.NewCmd( @@ -918,10 +918,10 @@ func (pa *path) startRecording() { nil) } }, - OnSegmentComplete: func(segmentPath string) { + OnSegmentComplete: func(path string) { if pa.conf.RunOnRecordSegmentComplete != "" { env := pa.externalCmdEnv() - env["MTX_SEGMENT_PATH"] = segmentPath + env["MTX_SEGMENT_PATH"] = path pa.Log(logger.Info, "runOnRecordSegmentComplete command launched") externalcmd.NewCmd( diff --git a/internal/record/agent.go b/internal/record/agent.go index 4632fcb3..e4503a37 100644 --- a/internal/record/agent.go +++ b/internal/record/agent.go @@ -11,7 +11,7 @@ import ( // Agent writes recordings to disk. type Agent struct { WriteQueueSize int - SegmentPathFormat string + PathFormat string Format conf.RecordFormat PartDuration time.Duration SegmentDuration time.Duration diff --git a/internal/record/agent_instance.go b/internal/record/agent_instance.go index 240445e1..e27fa50d 100644 --- a/internal/record/agent_instance.go +++ b/internal/record/agent_instance.go @@ -22,25 +22,25 @@ type sample struct { type agentInstance struct { agent *Agent - segmentPathFormat string - writer *asyncwriter.Writer - format format + pathFormat string + writer *asyncwriter.Writer + format format terminate chan struct{} done chan struct{} } 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 { case conf.RecordFormatMPEGTS: - a.segmentPathFormat += ".ts" + a.pathFormat += ".ts" default: - a.segmentPathFormat += ".mp4" + a.pathFormat += ".mp4" } a.terminate = make(chan struct{}) diff --git a/internal/record/agent_test.go b/internal/record/agent_test.go index eba83575..a2ee4ea7 100644 --- a/internal/record/agent_test.go +++ b/internal/record/agent_test.go @@ -156,13 +156,13 @@ func TestAgent(t *testing.T) { } w := &Agent{ - WriteQueueSize: 1024, - SegmentPathFormat: recordPath, - Format: f, - PartDuration: 100 * time.Millisecond, - SegmentDuration: 1 * time.Second, - PathName: "mypath", - Stream: stream, + WriteQueueSize: 1024, + PathFormat: recordPath, + Format: f, + PartDuration: 100 * time.Millisecond, + SegmentDuration: 1 * time.Second, + PathName: "mypath", + Stream: stream, OnSegmentCreate: func(fpath string) { segCreated <- struct{}{} }, @@ -266,14 +266,14 @@ func TestAgentFMP4NegativeDTS(t *testing.T) { recordPath := filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f") w := &Agent{ - WriteQueueSize: 1024, - SegmentPathFormat: recordPath, - Format: conf.RecordFormatFMP4, - PartDuration: 100 * time.Millisecond, - SegmentDuration: 1 * time.Second, - PathName: "mypath", - Stream: stream, - Parent: &nilLogger{}, + WriteQueueSize: 1024, + PathFormat: recordPath, + Format: conf.RecordFormatFMP4, + PartDuration: 100 * time.Millisecond, + SegmentDuration: 1 * time.Second, + PathName: "mypath", + Stream: stream, + Parent: &nilLogger{}, } w.Initialize() diff --git a/internal/record/cleaner.go b/internal/record/cleaner.go index 5051b86b..2d606459 100644 --- a/internal/record/cleaner.go +++ b/internal/record/cleaner.go @@ -41,9 +41,9 @@ func commonPath(v string) string { // CleanerEntry is a cleaner entry. type CleanerEntry struct { - SegmentPathFormat string - Format conf.RecordFormat - DeleteAfter time.Duration + PathFormat string + Format conf.RecordFormat + DeleteAfter time.Duration } // Cleaner removes expired recording segments from disk. @@ -106,21 +106,21 @@ func (c *Cleaner) doRun() { } func (c *Cleaner) doRunEntry(e *CleanerEntry) error { - segmentPathFormat := e.SegmentPathFormat + pathFormat := e.PathFormat switch e.Format { case conf.RecordFormatMPEGTS: - segmentPathFormat += ".ts" + pathFormat += ".ts" default: - segmentPathFormat += ".mp4" + pathFormat += ".mp4" } // we have to convert to absolute paths // 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() 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() { - var pa segmentPath - ok := pa.decode(segmentPathFormat, fpath) + var pa path + ok := pa.decode(pathFormat, fpath) if ok { - if now.Sub(pa.time) > e.DeleteAfter { + if now.Sub(time.Time(pa)) > e.DeleteAfter { c.Log(logger.Debug, "removing %s", fpath) os.Remove(fpath) } diff --git a/internal/record/cleaner_test.go b/internal/record/cleaner_test.go index b3d0978d..aca2bd37 100644 --- a/internal/record/cleaner_test.go +++ b/internal/record/cleaner_test.go @@ -33,9 +33,9 @@ func TestCleaner(t *testing.T) { c := &Cleaner{ Entries: []CleanerEntry{{ - SegmentPathFormat: recordPath, - Format: conf.RecordFormatFMP4, - DeleteAfter: 10 * time.Second, + PathFormat: recordPath, + Format: conf.RecordFormatFMP4, + DeleteAfter: 10 * time.Second, }}, Parent: nilLogger{}, } diff --git a/internal/record/format_fmp4_part.go b/internal/record/format_fmp4_part.go index 8ed48f58..bd21a904 100644 --- a/internal/record/format_fmp4_part.go +++ b/internal/record/format_fmp4_part.go @@ -65,7 +65,7 @@ func newFormatFMP4Part( func (p *formatFMP4Part) close() error { 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) err := os.MkdirAll(filepath.Dir(p.s.path), 0o755) diff --git a/internal/record/format_mpegts_segment.go b/internal/record/format_mpegts_segment.go index 4b4c9b18..e073fb5c 100644 --- a/internal/record/format_mpegts_segment.go +++ b/internal/record/format_mpegts_segment.go @@ -51,7 +51,7 @@ func (s *formatMPEGTSSegment) close() error { func (s *formatMPEGTSSegment) Write(p []byte) (int, error) { 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) err := os.MkdirAll(filepath.Dir(s.path), 0o755) diff --git a/internal/record/segment_path.go b/internal/record/path.go similarity index 73% rename from internal/record/segment_path.go rename to internal/record/path.go index ca79688c..c3bf2ae2 100644 --- a/internal/record/segment_path.go +++ b/internal/record/path.go @@ -21,11 +21,9 @@ func leadingZeros(v int, size int) string { return out2 + out } -type segmentPath struct { - time time.Time -} +type path time.Time -func (p *segmentPath) decode(format string, v string) bool { +func (p *path) decode(format string, v string) bool { re := format for _, ch := range []uint8{ @@ -143,22 +141,22 @@ func (p *segmentPath) decode(format string, v string) bool { } if unixSec > 0 { - p.time = time.Unix(unixSec, 0) + *p = path(time.Unix(unixSec, 0)) } 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 } -func (p segmentPath) encode(format string) string { - format = strings.ReplaceAll(format, "%Y", strconv.FormatInt(int64(p.time.Year()), 10)) - format = strings.ReplaceAll(format, "%m", leadingZeros(int(p.time.Month()), 2)) - format = strings.ReplaceAll(format, "%d", leadingZeros(p.time.Day(), 2)) - format = strings.ReplaceAll(format, "%H", leadingZeros(p.time.Hour(), 2)) - format = strings.ReplaceAll(format, "%M", leadingZeros(p.time.Minute(), 2)) - format = strings.ReplaceAll(format, "%S", leadingZeros(p.time.Second(), 2)) - format = strings.ReplaceAll(format, "%f", leadingZeros(p.time.Nanosecond()/1000, 6)) - format = strings.ReplaceAll(format, "%s", strconv.FormatInt(p.time.Unix(), 10)) +func (p path) encode(format string) string { + format = strings.ReplaceAll(format, "%Y", strconv.FormatInt(int64(time.Time(p).Year()), 10)) + format = strings.ReplaceAll(format, "%m", leadingZeros(int(time.Time(p).Month()), 2)) + format = strings.ReplaceAll(format, "%d", leadingZeros(time.Time(p).Day(), 2)) + format = strings.ReplaceAll(format, "%H", leadingZeros(time.Time(p).Hour(), 2)) + format = strings.ReplaceAll(format, "%M", leadingZeros(time.Time(p).Minute(), 2)) + format = strings.ReplaceAll(format, "%S", leadingZeros(time.Time(p).Second(), 2)) + format = strings.ReplaceAll(format, "%f", leadingZeros(time.Time(p).Nanosecond()/1000, 6)) + format = strings.ReplaceAll(format, "%s", strconv.FormatInt(time.Time(p).Unix(), 10)) return format } diff --git a/internal/record/segment_path_test.go b/internal/record/path_test.go similarity index 57% rename from internal/record/segment_path_test.go rename to internal/record/path_test.go index cb751ca7..0acdc661 100644 --- a/internal/record/segment_path_test.go +++ b/internal/record/path_test.go @@ -7,34 +7,30 @@ import ( "github.com/stretchr/testify/require" ) -var segmentPathCases = []struct { +var pathCases = []struct { name string format string - dec segmentPath + dec path enc string }{ { "standard", "%path/%Y-%m-%d_%H-%M-%S-%f.mp4", - segmentPath{ - time: time.Date(2008, 11, 0o7, 11, 22, 4, 123456000, time.Local), - }, + path(time.Date(2008, 11, 0o7, 11, 22, 4, 123456000, time.Local)), "%path/2008-11-07_11-22-04-123456.mp4", }, { "unix seconds", "%path/%s.mp4", - segmentPath{ - time: time.Date(2021, 12, 2, 12, 15, 23, 0, time.UTC).Local(), - }, + path(time.Date(2021, 12, 2, 12, 15, 23, 0, time.UTC).Local()), "%path/1638447323.mp4", }, } -func TestSegmentPathDecode(t *testing.T) { - for _, ca := range segmentPathCases { +func TestPathDecode(t *testing.T) { + for _, ca := range pathCases { t.Run(ca.name, func(t *testing.T) { - var dec segmentPath + var dec path ok := dec.decode(ca.format, ca.enc) require.Equal(t, true, ok) require.Equal(t, ca.dec, dec) @@ -42,8 +38,8 @@ func TestSegmentPathDecode(t *testing.T) { } } -func TestSegmentPathEncode(t *testing.T) { - for _, ca := range segmentPathCases { +func TestPathEncode(t *testing.T) { + for _, ca := range pathCases { t.Run(ca.name, func(t *testing.T) { require.Equal(t, ca.enc, ca.dec.encode(ca.format)) })