Ready-to-use SRT / WebRTC / RTSP / RTMP / LL-HLS media server and media proxy that allows to read, publish, proxy, record and playback video and audio streams.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

149 lines
3.2 KiB

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
}