mirror of
https://github.com/Monibuca/plugin-record.git
synced 2025-09-27 04:56:47 +08:00

* [feature] 支持录制完成后上传到Minio * change module id * Update mod name * reset go.mod * Update for minio uploading * Update for log * [feature] support all Recorder * Update * Merge branch 'v4' into githubv4 * v4: git commit for minio * fix error * Update * Update * Update for support max Duration * Update v4.6.5 * Update for chang Config name * [refactor] update for recording duration * Update for remove orgion file * Update mod * Update * fix: close mp4 record error * Update readme * Fix file not upload Successfully * feat(recording): 支持录制检查回调 * feat:增加数据库录制检查 * Update 录制文件没有写入结束标志 * 更新依赖包 * fix(record): 自动删除的录像文件。 * Update for sqllite to db error
174 lines
3.9 KiB
Go
174 lines
3.9 KiB
Go
package record
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
. "m7s.live/engine/v4"
|
|
"m7s.live/engine/v4/codec"
|
|
"m7s.live/engine/v4/codec/mpegts"
|
|
"m7s.live/engine/v4/util"
|
|
"m7s.live/plugin/hls/v4"
|
|
)
|
|
|
|
type HLSRecorder struct {
|
|
playlist hls.Playlist
|
|
tsStartTime uint32
|
|
tsLastTime uint32
|
|
tsTitle string
|
|
video_cc, audio_cc byte
|
|
Recorder
|
|
MemoryTs
|
|
}
|
|
|
|
func (h *HLSRecorder) SetId(string) {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
func (h *HLSRecorder) GetRecordModeString(mode RecordMode) string {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
func (h *HLSRecorder) StartWithDynamicTimeout(streamPath, fileName string, timeout time.Duration) error {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
func (h *HLSRecorder) UpdateTimeout(timeout time.Duration) {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
func NewHLSRecorder() (r *HLSRecorder) {
|
|
r = &HLSRecorder{}
|
|
r.Record = RecordPluginConfig.Hls
|
|
r.Storage = RecordPluginConfig.Storage
|
|
return r
|
|
}
|
|
|
|
func (h *HLSRecorder) Start(streamPath string) error {
|
|
h.ID = streamPath + "/hls"
|
|
return h.start(h, streamPath, SUBTYPE_RAW)
|
|
}
|
|
|
|
func (h *HLSRecorder) StartWithFileName(streamPath string, fileName string) error {
|
|
h.ID = streamPath + "/hls/" + fileName
|
|
return h.start(h, streamPath, SUBTYPE_RAW)
|
|
}
|
|
|
|
func (r *HLSRecorder) Close() (err error) {
|
|
if r.File != nil {
|
|
inf := hls.PlaylistInf{
|
|
Duration: float64(r.tsLastTime-r.tsStartTime) / 1000,
|
|
Title: r.tsTitle,
|
|
}
|
|
r.playlist.WriteInf(inf)
|
|
r.tsStartTime = 0
|
|
err = r.File.Close()
|
|
if err != nil {
|
|
r.Error("HLS File Close", zap.Error(err))
|
|
} else {
|
|
r.Info("HLS File Close", zap.Error(err))
|
|
go r.UploadFile(r.Path, r.filePath)
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
func (h *HLSRecorder) OnEvent(event any) {
|
|
var err error
|
|
defer func() {
|
|
if err != nil {
|
|
h.Stop(zap.Error(err))
|
|
}
|
|
}()
|
|
switch v := event.(type) {
|
|
case *HLSRecorder:
|
|
h.BytesPool = make(util.BytesPool, 17)
|
|
if h.Writer, err = h.Recorder.CreateFile(); err != nil {
|
|
return
|
|
}
|
|
h.SetIO(h.Writer)
|
|
h.playlist = hls.Playlist{
|
|
Writer: h.Writer,
|
|
Version: 3,
|
|
Sequence: 0,
|
|
Targetduration: int(math.Ceil(h.Fragment.Seconds())),
|
|
}
|
|
if err = h.playlist.Init(); err != nil {
|
|
return
|
|
}
|
|
if h.File, err = h.CreateFile(); err != nil {
|
|
return
|
|
}
|
|
case AudioFrame:
|
|
if h.tsStartTime == 0 {
|
|
h.tsStartTime = v.AbsTime
|
|
}
|
|
h.tsLastTime = v.AbsTime
|
|
h.Recorder.OnEvent(event)
|
|
pes := &mpegts.MpegtsPESFrame{
|
|
Pid: mpegts.PID_AUDIO,
|
|
IsKeyFrame: false,
|
|
ContinuityCounter: h.audio_cc,
|
|
ProgramClockReferenceBase: uint64(v.DTS),
|
|
}
|
|
h.WriteAudioFrame(v, pes)
|
|
_, err = h.BLL.WriteTo(h.File)
|
|
h.Recycle()
|
|
h.Clear()
|
|
h.audio_cc = pes.ContinuityCounter
|
|
case VideoFrame:
|
|
if h.tsStartTime == 0 {
|
|
h.tsStartTime = v.AbsTime
|
|
}
|
|
h.tsLastTime = v.AbsTime
|
|
h.Recorder.OnEvent(event)
|
|
pes := &mpegts.MpegtsPESFrame{
|
|
Pid: mpegts.PID_VIDEO,
|
|
IsKeyFrame: v.IFrame,
|
|
ContinuityCounter: h.video_cc,
|
|
ProgramClockReferenceBase: uint64(v.DTS),
|
|
}
|
|
if err = h.WriteVideoFrame(v, pes); err != nil {
|
|
return
|
|
}
|
|
_, err = h.BLL.WriteTo(h.File)
|
|
h.Recycle()
|
|
h.Clear()
|
|
h.video_cc = pes.ContinuityCounter
|
|
default:
|
|
h.Recorder.OnEvent(v)
|
|
}
|
|
}
|
|
|
|
// 创建一个新的ts文件
|
|
func (h *HLSRecorder) CreateFile() (fw FileWr, err error) {
|
|
h.tsTitle = fmt.Sprintf("%d.ts", time.Now().Unix())
|
|
filePath := filepath.Join(h.Stream.Path, h.tsTitle)
|
|
fw, err = h.CreateFileFn(filePath, false)
|
|
if err != nil {
|
|
h.Error("create file", zap.String("path", filePath), zap.Error(err))
|
|
return
|
|
}
|
|
h.Info("create file", zap.String("path", filePath))
|
|
|
|
if err = mpegts.WriteDefaultPATPacket(fw); err != nil {
|
|
return
|
|
}
|
|
var vcodec codec.VideoCodecID = 0
|
|
var acodec codec.AudioCodecID = 0
|
|
if h.Video != nil {
|
|
vcodec = h.Video.CodecID
|
|
}
|
|
if h.Audio != nil {
|
|
acodec = h.Audio.CodecID
|
|
}
|
|
mpegts.WritePMTPacket(fw, vcodec, acodec)
|
|
return
|
|
}
|