mirror of
https://github.com/bluenviron/mediamtx
synced 2025-02-27 00:50:29 +00:00
150 lines
3.2 KiB
Go
150 lines
3.2 KiB
Go
package playback
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/conf"
|
|
"github.com/bluenviron/mediamtx/internal/record"
|
|
)
|
|
|
|
// Segment is a recording segment.
|
|
type Segment struct {
|
|
Fpath string
|
|
Start time.Time
|
|
}
|
|
|
|
func findSegmentsInTimespan(
|
|
pathConf *conf.Path,
|
|
pathName string,
|
|
start time.Time,
|
|
duration time.Duration,
|
|
) ([]*Segment, error) {
|
|
if !pathConf.Playback {
|
|
return nil, fmt.Errorf("playback is disabled on path '%s'", pathName)
|
|
}
|
|
|
|
recordPath := record.PathAddExtension(
|
|
strings.ReplaceAll(pathConf.RecordPath, "%path", pathName),
|
|
pathConf.RecordFormat,
|
|
)
|
|
|
|
// we have to convert to absolute paths
|
|
// otherwise, recordPath and fpath inside Walk() won't have common elements
|
|
recordPath, _ = filepath.Abs(recordPath)
|
|
|
|
commonPath := record.CommonPath(recordPath)
|
|
end := start.Add(duration)
|
|
var segments []*Segment
|
|
|
|
err := filepath.Walk(commonPath, func(fpath string, info fs.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
var pa record.Path
|
|
ok := pa.Decode(recordPath, fpath)
|
|
|
|
// gather all segments that starts before the end of the playback
|
|
if ok && !end.Before(pa.Start) {
|
|
segments = append(segments, &Segment{
|
|
Fpath: fpath,
|
|
Start: pa.Start,
|
|
})
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if segments == nil {
|
|
return nil, errNoSegmentsFound
|
|
}
|
|
|
|
sort.Slice(segments, func(i, j int) bool {
|
|
return segments[i].Start.Before(segments[j].Start)
|
|
})
|
|
|
|
// find the segment that may contain the start of the playback and remove all previous ones
|
|
found := false
|
|
for i := 0; i < len(segments)-1; i++ {
|
|
if !start.Before(segments[i].Start) && start.Before(segments[i+1].Start) {
|
|
segments = segments[i:]
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// otherwise, keep the last segment only and check if it may contain the start of the playback
|
|
if !found {
|
|
segments = segments[len(segments)-1:]
|
|
if segments[len(segments)-1].Start.After(start) {
|
|
return nil, errNoSegmentsFound
|
|
}
|
|
}
|
|
|
|
return segments, nil
|
|
}
|
|
|
|
// FindSegments returns all segments of a path.
|
|
func FindSegments(
|
|
pathConf *conf.Path,
|
|
pathName string,
|
|
) ([]*Segment, error) {
|
|
if !pathConf.Playback {
|
|
return nil, fmt.Errorf("playback is disabled on path '%s'", pathName)
|
|
}
|
|
|
|
recordPath := record.PathAddExtension(
|
|
strings.ReplaceAll(pathConf.RecordPath, "%path", pathName),
|
|
pathConf.RecordFormat,
|
|
)
|
|
|
|
// we have to convert to absolute paths
|
|
// otherwise, recordPath and fpath inside Walk() won't have common elements
|
|
recordPath, _ = filepath.Abs(recordPath)
|
|
|
|
commonPath := record.CommonPath(recordPath)
|
|
var segments []*Segment
|
|
|
|
err := filepath.Walk(commonPath, func(fpath string, info fs.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
var pa record.Path
|
|
ok := pa.Decode(recordPath, fpath)
|
|
if ok {
|
|
segments = append(segments, &Segment{
|
|
Fpath: fpath,
|
|
Start: pa.Start,
|
|
})
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if segments == nil {
|
|
return nil, errNoSegmentsFound
|
|
}
|
|
|
|
sort.Slice(segments, func(i, j int) bool {
|
|
return segments[i].Start.Before(segments[j].Start)
|
|
})
|
|
|
|
return segments, nil
|
|
}
|