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.
 
 
 
 
 
 

124 lines
2.5 KiB

package record
import (
"context"
"strings"
"time"
"github.com/bluenviron/mediacommon/pkg/formats/fmp4"
"github.com/bluenviron/mediamtx/internal/asyncwriter"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/stream"
)
// OnSegmentFunc is the prototype of the function passed as runOnSegmentStart / runOnSegmentComplete
type OnSegmentFunc = func(string)
type sample struct {
*fmp4.PartSample
dts time.Duration
}
// Agent saves streams on disk.
type Agent struct {
path string
partDuration time.Duration
segmentDuration time.Duration
stream *stream.Stream
onSegmentCreate OnSegmentFunc
onSegmentComplete OnSegmentFunc
parent logger.Writer
ctx context.Context
ctxCancel func()
writer *asyncwriter.Writer
format recFormat
done chan struct{}
}
// NewAgent allocates an Agent.
func NewAgent(
writeQueueSize int,
path string,
format conf.RecordFormat,
partDuration time.Duration,
segmentDuration time.Duration,
pathName string,
stream *stream.Stream,
onSegmentCreate OnSegmentFunc,
onSegmentComplete OnSegmentFunc,
parent logger.Writer,
) *Agent {
path = strings.ReplaceAll(path, "%path", pathName)
switch format {
case conf.RecordFormatMPEGTS:
path += ".ts"
default:
path += ".mp4"
}
ctx, ctxCancel := context.WithCancel(context.Background())
a := &Agent{
path: path,
partDuration: partDuration,
segmentDuration: segmentDuration,
stream: stream,
onSegmentCreate: onSegmentCreate,
onSegmentComplete: onSegmentComplete,
parent: parent,
ctx: ctx,
ctxCancel: ctxCancel,
done: make(chan struct{}),
}
a.writer = asyncwriter.New(writeQueueSize, a)
switch format {
case conf.RecordFormatMPEGTS:
a.format = newRecFormatMPEGTS(a)
default:
a.format = newRecFormatFMP4(a)
}
go a.run()
return a
}
// Close closes the Agent.
func (a *Agent) Close() {
a.Log(logger.Info, "recording stopped")
a.ctxCancel()
<-a.done
}
// Log is the main logging function.
func (a *Agent) Log(level logger.Level, format string, args ...interface{}) {
a.parent.Log(level, "[record] "+format, args...)
}
func (a *Agent) run() {
defer close(a.done)
a.writer.Start()
select {
case err := <-a.writer.Error():
a.Log(logger.Error, err.Error())
a.stream.RemoveReader(a.writer)
case <-a.ctx.Done():
a.stream.RemoveReader(a.writer)
a.writer.Stop()
}
a.format.close()
}