@@ -35,6 +35,7 @@ type Options struct {
3535 HardwareDevice string
3636 BufferPauseThreshold time.Duration
3737 BufferResumeThreshold time.Duration
38+ SegmentRetention time.Duration
3839 BufferCheckInterval time.Duration
3940 IdleTimeout time.Duration
4041 ReapInterval time.Duration
@@ -53,6 +54,7 @@ type Request struct {
5354 StartTimeTicks int64
5455 RequestedStartTimeTicks int64
5556 SegmentStartIndex int
57+ SegmentRequest bool
5658 Media MediaInfo
5759}
5860
@@ -73,6 +75,7 @@ type Session struct {
7375 StartTimeTicks int64
7476 RequestedStartTimeTicks int64
7577 SegmentStartIndex int
78+ OldestSegmentKept int
7679 HighestSegmentSeen int
7780 Media MediaInfo
7881 Dir string
@@ -155,6 +158,9 @@ func normalizeManagerOptions(options Options) Options {
155158 if options .BufferResumeThreshold <= 0 {
156159 options .BufferResumeThreshold = 2 * time .Minute
157160 }
161+ if options .SegmentRetention <= 0 {
162+ options .SegmentRetention = 5 * time .Minute
163+ }
158164 if options .BufferCheckInterval <= 0 {
159165 options .BufferCheckInterval = time .Second
160166 }
@@ -410,6 +416,7 @@ func (m *Manager) RecordProgress(event PlaybackEvent) int {
410416 now := time .Now ()
411417 count := 0
412418 var actions []bufferAction
419+ var cleanups []segmentCleanupTask
413420 for _ , session := range m .sessions {
414421 if ! matchesPlayback (session , event ) {
415422 continue
@@ -430,9 +437,15 @@ func (m *Manager) RecordProgress(event PlaybackEvent) int {
430437 if action , ok := m .bufferActionLocked (session ); ok {
431438 actions = append (actions , action )
432439 }
440+ if cleanup , ok := m .segmentCleanupTaskLocked (session ); ok {
441+ cleanups = append (cleanups , cleanup )
442+ }
433443 count ++
434444 }
435445 m .mu .Unlock ()
446+ for _ , cleanup := range cleanups {
447+ cleanupOldSegments (cleanup )
448+ }
436449 for _ , action := range actions {
437450 m .applyBufferAction (action )
438451 }
@@ -535,6 +548,9 @@ func (m *Manager) ReconcileBuffers() {
535548}
536549
537550func shouldRestart (session * Session , request Request ) bool {
551+ if request .SegmentRequest && session .OldestSegmentKept > session .SegmentStartIndex && request .SegmentStartIndex < session .OldestSegmentKept {
552+ return true
553+ }
538554 if request .StartTimeTicks != session .StartTimeTicks {
539555 return true
540556 }
@@ -571,6 +587,9 @@ func touchSession(session *Session, request Request, now time.Time, mediaAccess
571587 session .StartTimeTicks = request .StartTimeTicks
572588 session .RequestedStartTimeTicks = request .RequestedStartTimeTicks
573589 session .SegmentStartIndex = request .SegmentStartIndex
590+ if session .OldestSegmentKept < request .SegmentStartIndex {
591+ session .OldestSegmentKept = request .SegmentStartIndex
592+ }
574593 if session .HighestSegmentSeen < request .SegmentStartIndex {
575594 session .HighestSegmentSeen = request .SegmentStartIndex
576595 }
@@ -591,6 +610,61 @@ type bufferAction struct {
591610 bufferTicks int64
592611}
593612
613+ type segmentCleanupTask struct {
614+ sessionID string
615+ dir string
616+ beforeSegment int
617+ }
618+
619+ func (m * Manager ) segmentCleanupTaskLocked (session * Session ) (segmentCleanupTask , bool ) {
620+ if session == nil || session .Dir == "" || m .options .SegmentRetention <= 0 {
621+ return segmentCleanupTask {}, false
622+ }
623+ retentionTicks := durationToTicks (m .options .SegmentRetention )
624+ if retentionTicks <= 0 || session .PositionTicks <= retentionTicks {
625+ return segmentCleanupTask {}, false
626+ }
627+ cutoffTicks := session .PositionTicks - retentionTicks
628+ beforeSegment := int (cutoffTicks / defaultSegmentTicks )
629+ if beforeSegment <= session .OldestSegmentKept {
630+ return segmentCleanupTask {}, false
631+ }
632+ session .OldestSegmentKept = beforeSegment
633+ return segmentCleanupTask {sessionID : session .ID , dir : session .Dir , beforeSegment : beforeSegment }, true
634+ }
635+
636+ func cleanupOldSegments (task segmentCleanupTask ) {
637+ entries , err := os .ReadDir (task .dir )
638+ if err != nil {
639+ if ! os .IsNotExist (err ) {
640+ logging .Errorf ("transcode cleanup error id=%s err=%v" , task .sessionID , err )
641+ }
642+ return
643+ }
644+
645+ deleted := 0
646+ for _ , entry := range entries {
647+ if entry .IsDir () {
648+ continue
649+ }
650+ segmentIndex , ok := segmentIndexFromName (entry .Name ())
651+ if ! ok || segmentIndex >= task .beforeSegment {
652+ continue
653+ }
654+ path := filepath .Join (task .dir , entry .Name ())
655+ if err := os .Remove (path ); err != nil {
656+ if ! os .IsNotExist (err ) {
657+ logging .Errorf ("transcode cleanup error id=%s file=%s err=%v" , task .sessionID , entry .Name (), err )
658+ }
659+ continue
660+ }
661+ deleted ++
662+ }
663+ if deleted > 0 {
664+ logging .Infof ("transcode cleanup id=%s deleted_segments=%d before_segment=%d" , task .sessionID , deleted , task .beforeSegment )
665+ }
666+ }
667+
594668func (m * Manager ) bufferActionLocked (session * Session ) (bufferAction , bool ) {
595669 if session == nil || session .process == nil {
596670 return bufferAction {}, false
0 commit comments