123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- 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 NewHLSRecorder() (r *HLSRecorder) {
- r = &HLSRecorder{}
- r.Record = RecordPluginConfig.Hls
- return r
- }
- func (h *HLSRecorder) Start(streamPath string) error {
- h.ID = streamPath + "/hls"
- 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()
- }
- return
- }
- 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
- }
|