fmp4.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package record
  2. import (
  3. "github.com/Eyevinn/mp4ff/aac"
  4. "github.com/Eyevinn/mp4ff/mp4"
  5. . "m7s.live/engine/v4"
  6. "m7s.live/engine/v4/codec"
  7. )
  8. type mediaContext struct {
  9. trackId uint32
  10. fragment *mp4.Fragment
  11. ts uint32 // 每个小片段起始时间戳
  12. }
  13. func (m *mediaContext) push(recoder *FMP4Recorder, dt uint32, dur uint32, data []byte, flags uint32) {
  14. if m.fragment != nil && dt-m.ts > 1000 {
  15. m.fragment.Encode(recoder.File)
  16. m.fragment = nil
  17. }
  18. if m.fragment == nil {
  19. recoder.seqNumber++
  20. m.fragment, _ = mp4.CreateFragment(recoder.seqNumber, m.trackId)
  21. m.ts = dt
  22. }
  23. m.fragment.AddFullSample(mp4.FullSample{
  24. Data: data,
  25. DecodeTime: uint64(dt),
  26. Sample: mp4.Sample{
  27. Flags: flags,
  28. Dur: dur,
  29. Size: uint32(len(data)),
  30. },
  31. })
  32. }
  33. type FMP4Recorder struct {
  34. Recorder
  35. initSegment *mp4.InitSegment `json:"-" yaml:"-"`
  36. video mediaContext
  37. audio mediaContext
  38. seqNumber uint32
  39. ftyp *mp4.FtypBox
  40. }
  41. func NewFMP4Recorder() *FMP4Recorder {
  42. r := &FMP4Recorder{}
  43. r.Record = RecordPluginConfig.Fmp4
  44. return r
  45. }
  46. func (r *FMP4Recorder) Start(streamPath string) (err error) {
  47. r.ID = streamPath + "/fmp4"
  48. return r.start(r, streamPath, SUBTYPE_RAW)
  49. }
  50. func (r *FMP4Recorder) Close() error {
  51. if r.File != nil {
  52. if r.video.fragment != nil {
  53. r.video.fragment.Encode(r.File)
  54. r.video.fragment = nil
  55. }
  56. if r.audio.fragment != nil {
  57. r.audio.fragment.Encode(r.File)
  58. r.audio.fragment = nil
  59. }
  60. r.File.Close()
  61. }
  62. return nil
  63. }
  64. func (r *FMP4Recorder) OnEvent(event any) {
  65. r.Recorder.OnEvent(event)
  66. switch v := event.(type) {
  67. case FileWr:
  68. r.initSegment = mp4.CreateEmptyInit()
  69. r.initSegment.Moov.Mvhd.NextTrackID = 1
  70. if r.VideoReader != nil {
  71. moov := r.initSegment.Moov
  72. trackID := moov.Mvhd.NextTrackID
  73. moov.Mvhd.NextTrackID++
  74. newTrak := mp4.CreateEmptyTrak(trackID, 1000, "video", "chi")
  75. moov.AddChild(newTrak)
  76. moov.Mvex.AddChild(mp4.CreateTrex(trackID))
  77. r.video.trackId = trackID
  78. switch r.Video.CodecID {
  79. case codec.CodecID_H264:
  80. r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
  81. "isom", "iso2", "avc1", "mp41",
  82. })
  83. newTrak.SetAVCDescriptor("avc1", r.Video.ParamaterSets[0:1], r.Video.ParamaterSets[1:2], true)
  84. case codec.CodecID_H265:
  85. r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
  86. "isom", "iso2", "hvc1", "mp41",
  87. })
  88. newTrak.SetHEVCDescriptor("hvc1", r.Video.ParamaterSets[0:1], r.Video.ParamaterSets[1:2], r.Video.ParamaterSets[2:3], nil, true)
  89. }
  90. }
  91. if r.AudioReader != nil {
  92. moov := r.initSegment.Moov
  93. trackID := moov.Mvhd.NextTrackID
  94. moov.Mvhd.NextTrackID++
  95. newTrak := mp4.CreateEmptyTrak(trackID, 1000, "audio", "chi")
  96. moov.AddChild(newTrak)
  97. moov.Mvex.AddChild(mp4.CreateTrex(trackID))
  98. r.audio.trackId = trackID
  99. switch r.Audio.CodecID {
  100. case codec.CodecID_AAC:
  101. switch r.Audio.AudioObjectType {
  102. case 1:
  103. newTrak.SetAACDescriptor(aac.HEAACv1, int(r.Audio.SampleRate))
  104. case 2:
  105. newTrak.SetAACDescriptor(aac.AAClc, int(r.Audio.SampleRate))
  106. case 3:
  107. newTrak.SetAACDescriptor(aac.HEAACv2, int(r.Audio.SampleRate))
  108. }
  109. case codec.CodecID_PCMA:
  110. stsd := newTrak.Mdia.Minf.Stbl.Stsd
  111. pcma := mp4.CreateAudioSampleEntryBox("pcma",
  112. uint16(r.Audio.Channels),
  113. uint16(r.Audio.SampleSize), uint16(r.Audio.SampleRate), nil)
  114. stsd.AddChild(pcma)
  115. case codec.CodecID_PCMU:
  116. stsd := newTrak.Mdia.Minf.Stbl.Stsd
  117. pcmu := mp4.CreateAudioSampleEntryBox("pcmu",
  118. uint16(r.Audio.Channels),
  119. uint16(r.Audio.SampleSize), uint16(r.Audio.SampleRate), nil)
  120. stsd.AddChild(pcmu)
  121. }
  122. }
  123. if r.ftyp == nil {
  124. r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
  125. "isom", "iso2", "avc1", "mp41",
  126. })
  127. }
  128. r.ftyp.Encode(v)
  129. r.initSegment.Moov.Encode(v)
  130. r.seqNumber = 0
  131. case AudioFrame:
  132. if r.audio.trackId != 0 {
  133. r.audio.push(r, v.AbsTime, v.DeltaTime, v.AUList.ToBytes(), mp4.SyncSampleFlags)
  134. }
  135. case VideoFrame:
  136. if r.video.trackId != 0 {
  137. flag := mp4.NonSyncSampleFlags
  138. if v.IFrame {
  139. flag = mp4.SyncSampleFlags
  140. }
  141. if data := v.AVCC.ToBytes(); len(data) > 5 {
  142. r.video.push(r, v.AbsTime, v.DeltaTime, data[5:], flag)
  143. }
  144. }
  145. }
  146. }