hls.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package record
  2. import (
  3. "fmt"
  4. "math"
  5. "path/filepath"
  6. "time"
  7. "go.uber.org/zap"
  8. . "m7s.live/engine/v4"
  9. "m7s.live/engine/v4/codec"
  10. "m7s.live/engine/v4/codec/mpegts"
  11. "m7s.live/engine/v4/util"
  12. "m7s.live/plugin/hls/v4"
  13. )
  14. type HLSRecorder struct {
  15. playlist hls.Playlist
  16. tsStartTime uint32
  17. tsLastTime uint32
  18. tsTitle string
  19. video_cc, audio_cc byte
  20. Recorder
  21. MemoryTs
  22. }
  23. func NewHLSRecorder() (r *HLSRecorder) {
  24. r = &HLSRecorder{}
  25. r.Record = RecordPluginConfig.Hls
  26. return r
  27. }
  28. func (h *HLSRecorder) Start(streamPath string) error {
  29. h.ID = streamPath + "/hls"
  30. return h.start(h, streamPath, SUBTYPE_RAW)
  31. }
  32. func (r *HLSRecorder) Close() (err error) {
  33. if r.File != nil {
  34. inf := hls.PlaylistInf{
  35. Duration: float64(r.tsLastTime-r.tsStartTime) / 1000,
  36. Title: r.tsTitle,
  37. }
  38. r.playlist.WriteInf(inf)
  39. r.tsStartTime = 0
  40. err = r.File.Close()
  41. }
  42. return
  43. }
  44. func (h *HLSRecorder) OnEvent(event any) {
  45. var err error
  46. defer func() {
  47. if err != nil {
  48. h.Stop(zap.Error(err))
  49. }
  50. }()
  51. switch v := event.(type) {
  52. case *HLSRecorder:
  53. h.BytesPool = make(util.BytesPool, 17)
  54. if h.Writer, err = h.Recorder.CreateFile(); err != nil {
  55. return
  56. }
  57. h.SetIO(h.Writer)
  58. h.playlist = hls.Playlist{
  59. Writer: h.Writer,
  60. Version: 3,
  61. Sequence: 0,
  62. Targetduration: int(math.Ceil(h.Fragment.Seconds())),
  63. }
  64. if err = h.playlist.Init(); err != nil {
  65. return
  66. }
  67. if h.File, err = h.CreateFile(); err != nil {
  68. return
  69. }
  70. case AudioFrame:
  71. if h.tsStartTime == 0 {
  72. h.tsStartTime = v.AbsTime
  73. }
  74. h.tsLastTime = v.AbsTime
  75. h.Recorder.OnEvent(event)
  76. pes := &mpegts.MpegtsPESFrame{
  77. Pid: mpegts.PID_AUDIO,
  78. IsKeyFrame: false,
  79. ContinuityCounter: h.audio_cc,
  80. ProgramClockReferenceBase: uint64(v.DTS),
  81. }
  82. h.WriteAudioFrame(v, pes)
  83. _, err = h.BLL.WriteTo(h.File)
  84. h.Recycle()
  85. h.Clear()
  86. h.audio_cc = pes.ContinuityCounter
  87. case VideoFrame:
  88. if h.tsStartTime == 0 {
  89. h.tsStartTime = v.AbsTime
  90. }
  91. h.tsLastTime = v.AbsTime
  92. h.Recorder.OnEvent(event)
  93. pes := &mpegts.MpegtsPESFrame{
  94. Pid: mpegts.PID_VIDEO,
  95. IsKeyFrame: v.IFrame,
  96. ContinuityCounter: h.video_cc,
  97. ProgramClockReferenceBase: uint64(v.DTS),
  98. }
  99. if err = h.WriteVideoFrame(v, pes); err != nil {
  100. return
  101. }
  102. _, err = h.BLL.WriteTo(h.File)
  103. h.Recycle()
  104. h.Clear()
  105. h.video_cc = pes.ContinuityCounter
  106. default:
  107. h.Recorder.OnEvent(v)
  108. }
  109. }
  110. // 创建一个新的ts文件
  111. func (h *HLSRecorder) CreateFile() (fw FileWr, err error) {
  112. h.tsTitle = fmt.Sprintf("%d.ts", time.Now().Unix())
  113. filePath := filepath.Join(h.Stream.Path, h.tsTitle)
  114. fw, err = h.CreateFileFn(filePath, false)
  115. if err != nil {
  116. h.Error("create file", zap.String("path", filePath), zap.Error(err))
  117. return
  118. }
  119. h.Info("create file", zap.String("path", filePath))
  120. if err = mpegts.WriteDefaultPATPacket(fw); err != nil {
  121. return
  122. }
  123. var vcodec codec.VideoCodecID = 0
  124. var acodec codec.AudioCodecID = 0
  125. if h.Video != nil {
  126. vcodec = h.Video.CodecID
  127. }
  128. if h.Audio != nil {
  129. acodec = h.Audio.CodecID
  130. }
  131. mpegts.WritePMTPacket(fw, vcodec, acodec)
  132. return
  133. }