golanggohlsrtmpwebrtcmedia-serverobs-studiortcprtmp-proxyrtmp-serverrtprtsprtsp-proxyrtsp-relayrtsp-serversrtstreamingwebrtc-proxy
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
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() |
|
}
|
|
|