flv.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package record
  2. import (
  3. "io"
  4. "net"
  5. "os"
  6. "time"
  7. "go.uber.org/zap"
  8. . "m7s.live/engine/v4"
  9. "m7s.live/engine/v4/codec"
  10. "m7s.live/engine/v4/util"
  11. )
  12. type FLVRecorder struct {
  13. Recorder
  14. filepositions []uint64
  15. times []float64
  16. Offset int64
  17. duration int64
  18. }
  19. func NewFLVRecorder() (r *FLVRecorder) {
  20. r = &FLVRecorder{}
  21. r.Record = RecordPluginConfig.Flv
  22. return r
  23. }
  24. func (r *FLVRecorder) Start(streamPath string) (err error) {
  25. r.ID = streamPath + "/flv"
  26. return r.start(r, streamPath, SUBTYPE_FLV)
  27. }
  28. func (r *FLVRecorder) writeMetaData(file FileWr, duration int64) {
  29. defer file.Close()
  30. at, vt := r.Audio, r.Video
  31. hasAudio, hasVideo := at != nil, vt != nil
  32. var amf util.AMF
  33. metaData := util.EcmaArray{
  34. "MetaDataCreator": "m7s " + Engine.Version,
  35. "hasVideo": hasVideo,
  36. "hasAudio": hasAudio,
  37. "hasMatadata": true,
  38. "canSeekToEnd": false,
  39. "duration": float64(duration) / 1000,
  40. "hasKeyFrames": len(r.filepositions) > 0,
  41. "filesize": 0,
  42. }
  43. var flags byte
  44. if hasAudio {
  45. flags |= (1 << 2)
  46. metaData["audiocodecid"] = int(at.CodecID)
  47. metaData["audiosamplerate"] = at.SampleRate
  48. metaData["audiosamplesize"] = at.SampleSize
  49. metaData["stereo"] = at.Channels == 2
  50. }
  51. if hasVideo {
  52. flags |= 1
  53. metaData["videocodecid"] = int(vt.CodecID)
  54. metaData["width"] = vt.SPSInfo.Width
  55. metaData["height"] = vt.SPSInfo.Height
  56. metaData["framerate"] = vt.FPS
  57. metaData["videodatarate"] = vt.BPS
  58. metaData["keyframes"] = map[string]any{
  59. "filepositions": r.filepositions,
  60. "times": r.times,
  61. }
  62. defer func() {
  63. r.filepositions = []uint64{0}
  64. r.times = []float64{0}
  65. }()
  66. }
  67. amf.Marshals("onMetaData", metaData)
  68. offset := amf.Len() + len(codec.FLVHeader) + 15
  69. if keyframesCount := len(r.filepositions); keyframesCount > 0 {
  70. metaData["filesize"] = uint64(offset) + r.filepositions[keyframesCount-1]
  71. for i := range r.filepositions {
  72. r.filepositions[i] += uint64(offset)
  73. }
  74. metaData["keyframes"] = map[string]any{
  75. "filepositions": r.filepositions,
  76. "times": r.times,
  77. }
  78. }
  79. if tempFile, err := os.CreateTemp("", "*.flv"); err != nil {
  80. r.Error("create temp file failed: ", zap.Error(err))
  81. return
  82. } else {
  83. defer func() {
  84. tempFile.Close()
  85. os.Remove(tempFile.Name())
  86. r.Info("writeMetaData success")
  87. }()
  88. _, err := tempFile.Write([]byte{'F', 'L', 'V', 0x01, flags, 0, 0, 0, 9, 0, 0, 0, 0})
  89. if err != nil {
  90. r.Error("", zap.Error(err))
  91. return
  92. }
  93. amf.Reset()
  94. marshals := amf.Marshals("onMetaData", metaData)
  95. codec.WriteFLVTag(tempFile, codec.FLV_TAG_TYPE_SCRIPT, 0, marshals)
  96. _, err = file.Seek(int64(len(codec.FLVHeader)), io.SeekStart)
  97. if err != nil {
  98. r.Error("writeMetaData Seek failed: ", zap.Error(err))
  99. return
  100. }
  101. _, err = io.Copy(tempFile, file)
  102. if err != nil {
  103. r.Error("writeMetaData Copy failed: ", zap.Error(err))
  104. return
  105. }
  106. tempFile.Seek(0, io.SeekStart)
  107. file.Seek(0, io.SeekStart)
  108. _, err = io.Copy(file, tempFile)
  109. if err != nil {
  110. r.Error("writeMetaData Copy failed: ", zap.Error(err))
  111. return
  112. }
  113. }
  114. }
  115. func (r *FLVRecorder) OnEvent(event any) {
  116. r.Recorder.OnEvent(event)
  117. switch v := event.(type) {
  118. case FileWr:
  119. // 写入文件头
  120. if !r.append {
  121. v.Write(codec.FLVHeader)
  122. } else {
  123. if _, err := v.Seek(-4, io.SeekEnd); err != nil {
  124. r.Error("seek file failed", zap.Error(err))
  125. v.Write(codec.FLVHeader)
  126. } else {
  127. tmp := make(util.Buffer, 4)
  128. tmp2 := tmp
  129. v.Read(tmp)
  130. tagSize := tmp.ReadUint32()
  131. tmp = tmp2
  132. v.Seek(int64(tagSize), io.SeekEnd)
  133. v.Read(tmp2)
  134. ts := tmp2.ReadUint24() | (uint32(tmp[3]) << 24)
  135. r.Info("append flv", zap.Uint32("last tagSize", tagSize), zap.Uint32("last ts", ts))
  136. if r.VideoReader != nil {
  137. r.VideoReader.StartTs = time.Duration(ts) * time.Millisecond
  138. }
  139. if r.AudioReader != nil {
  140. r.AudioReader.StartTs = time.Duration(ts) * time.Millisecond
  141. }
  142. v.Seek(0, io.SeekEnd)
  143. }
  144. }
  145. case FLVFrame:
  146. check := false
  147. var absTime uint32
  148. if r.VideoReader == nil {
  149. check = true
  150. absTime = r.AudioReader.AbsTime
  151. } else if v.IsVideo() {
  152. check = r.VideoReader.Value.IFrame
  153. absTime = r.VideoReader.AbsTime
  154. if check {
  155. r.filepositions = append(r.filepositions, uint64(r.Offset))
  156. r.times = append(r.times, float64(absTime)/1000)
  157. }
  158. }
  159. if r.duration = int64(absTime); r.Fragment > 0 && check && time.Duration(r.duration)*time.Millisecond >= r.Fragment {
  160. r.Close()
  161. r.Offset = 0
  162. if file, err := r.CreateFile(); err == nil {
  163. r.File = file
  164. file.Write(codec.FLVHeader)
  165. var dcflv net.Buffers
  166. if r.VideoReader != nil {
  167. r.VideoReader.ResetAbsTime()
  168. dcflv = codec.VideoAVCC2FLV(0, r.VideoReader.Track.SequenceHead)
  169. flv := append(dcflv, codec.VideoAVCC2FLV(0, r.VideoReader.Value.AVCC.ToBuffers()...)...)
  170. flv.WriteTo(file)
  171. }
  172. if r.AudioReader != nil {
  173. r.AudioReader.ResetAbsTime()
  174. if r.Audio.CodecID == codec.CodecID_AAC {
  175. dcflv = codec.AudioAVCC2FLV(0, r.AudioReader.Track.SequenceHead)
  176. }
  177. flv := append(dcflv, codec.AudioAVCC2FLV(0, r.AudioReader.Value.AVCC.ToBuffers()...)...)
  178. flv.WriteTo(file)
  179. }
  180. return
  181. }
  182. }
  183. if n, err := v.WriteTo(r.File); err != nil {
  184. r.Error("write file failed", zap.Error(err))
  185. r.Stop(zap.Error(err))
  186. } else {
  187. r.Offset += n
  188. }
  189. }
  190. }
  191. func (r *FLVRecorder) Close() error {
  192. if r.File != nil {
  193. if !r.append {
  194. go r.writeMetaData(r.File, r.duration)
  195. } else {
  196. return r.File.Close()
  197. }
  198. }
  199. return nil
  200. }