123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- package record
- import (
- "github.com/Eyevinn/mp4ff/aac"
- "github.com/Eyevinn/mp4ff/mp4"
- . "m7s.live/engine/v4"
- "m7s.live/engine/v4/codec"
- )
- type mediaContext struct {
- trackId uint32
- fragment *mp4.Fragment
- ts uint32 // 每个小片段起始时间戳
- }
- func (m *mediaContext) push(recoder *FMP4Recorder, dt uint32, dur uint32, data []byte, flags uint32) {
- if m.fragment != nil && dt-m.ts > 1000 {
- m.fragment.Encode(recoder.File)
- m.fragment = nil
- }
- if m.fragment == nil {
- recoder.seqNumber++
- m.fragment, _ = mp4.CreateFragment(recoder.seqNumber, m.trackId)
- m.ts = dt
- }
- m.fragment.AddFullSample(mp4.FullSample{
- Data: data,
- DecodeTime: uint64(dt),
- Sample: mp4.Sample{
- Flags: flags,
- Dur: dur,
- Size: uint32(len(data)),
- },
- })
- }
- type FMP4Recorder struct {
- Recorder
- initSegment *mp4.InitSegment `json:"-" yaml:"-"`
- video mediaContext
- audio mediaContext
- seqNumber uint32
- ftyp *mp4.FtypBox
- }
- func NewFMP4Recorder() *FMP4Recorder {
- r := &FMP4Recorder{}
- r.Record = RecordPluginConfig.Fmp4
- return r
- }
- func (r *FMP4Recorder) Start(streamPath string) (err error) {
- r.ID = streamPath + "/fmp4"
- return r.start(r, streamPath, SUBTYPE_RAW)
- }
- func (r *FMP4Recorder) Close() error {
- if r.File != nil {
- if r.video.fragment != nil {
- r.video.fragment.Encode(r.File)
- r.video.fragment = nil
- }
- if r.audio.fragment != nil {
- r.audio.fragment.Encode(r.File)
- r.audio.fragment = nil
- }
- r.File.Close()
- }
- return nil
- }
- func (r *FMP4Recorder) OnEvent(event any) {
- r.Recorder.OnEvent(event)
- switch v := event.(type) {
- case FileWr:
- r.initSegment = mp4.CreateEmptyInit()
- r.initSegment.Moov.Mvhd.NextTrackID = 1
- if r.VideoReader != nil {
- moov := r.initSegment.Moov
- trackID := moov.Mvhd.NextTrackID
- moov.Mvhd.NextTrackID++
- newTrak := mp4.CreateEmptyTrak(trackID, 1000, "video", "chi")
- moov.AddChild(newTrak)
- moov.Mvex.AddChild(mp4.CreateTrex(trackID))
- r.video.trackId = trackID
- switch r.Video.CodecID {
- case codec.CodecID_H264:
- r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
- "isom", "iso2", "avc1", "mp41",
- })
- newTrak.SetAVCDescriptor("avc1", r.Video.ParamaterSets[0:1], r.Video.ParamaterSets[1:2], true)
- case codec.CodecID_H265:
- r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
- "isom", "iso2", "hvc1", "mp41",
- })
- newTrak.SetHEVCDescriptor("hvc1", r.Video.ParamaterSets[0:1], r.Video.ParamaterSets[1:2], r.Video.ParamaterSets[2:3], nil, true)
- }
- }
- if r.AudioReader != nil {
- moov := r.initSegment.Moov
- trackID := moov.Mvhd.NextTrackID
- moov.Mvhd.NextTrackID++
- newTrak := mp4.CreateEmptyTrak(trackID, 1000, "audio", "chi")
- moov.AddChild(newTrak)
- moov.Mvex.AddChild(mp4.CreateTrex(trackID))
- r.audio.trackId = trackID
- switch r.Audio.CodecID {
- case codec.CodecID_AAC:
- switch r.Audio.AudioObjectType {
- case 1:
- newTrak.SetAACDescriptor(aac.HEAACv1, int(r.Audio.SampleRate))
- case 2:
- newTrak.SetAACDescriptor(aac.AAClc, int(r.Audio.SampleRate))
- case 3:
- newTrak.SetAACDescriptor(aac.HEAACv2, int(r.Audio.SampleRate))
- }
- case codec.CodecID_PCMA:
- stsd := newTrak.Mdia.Minf.Stbl.Stsd
- pcma := mp4.CreateAudioSampleEntryBox("pcma",
- uint16(r.Audio.Channels),
- uint16(r.Audio.SampleSize), uint16(r.Audio.SampleRate), nil)
- stsd.AddChild(pcma)
- case codec.CodecID_PCMU:
- stsd := newTrak.Mdia.Minf.Stbl.Stsd
- pcmu := mp4.CreateAudioSampleEntryBox("pcmu",
- uint16(r.Audio.Channels),
- uint16(r.Audio.SampleSize), uint16(r.Audio.SampleRate), nil)
- stsd.AddChild(pcmu)
- }
- }
- if r.ftyp == nil {
- r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
- "isom", "iso2", "avc1", "mp41",
- })
- }
- r.ftyp.Encode(v)
- r.initSegment.Moov.Encode(v)
- r.seqNumber = 0
- case AudioFrame:
- if r.audio.trackId != 0 {
- r.audio.push(r, v.AbsTime, v.DeltaTime, v.AUList.ToBytes(), mp4.SyncSampleFlags)
- }
- case VideoFrame:
- if r.video.trackId != 0 {
- flag := mp4.NonSyncSampleFlags
- if v.IFrame {
- flag = mp4.SyncSampleFlags
- }
- if data := v.AVCC.ToBytes(); len(data) > 5 {
- r.video.push(r, v.AbsTime, v.DeltaTime, data[5:], flag)
- }
- }
- }
- }
|