mirror of
https://github.com/Monibuca/plugin-record.git
synced 2025-10-05 16:56:53 +08:00
152 lines
3.9 KiB
Go
152 lines
3.9 KiB
Go
package mp4
|
|
|
|
import (
|
|
"github.com/edgeware/mp4ff/aac"
|
|
"github.com/edgeware/mp4ff/mp4"
|
|
. "m7s.live/engine/v4"
|
|
"m7s.live/engine/v4/codec"
|
|
"m7s.live/engine/v4/config"
|
|
"m7s.live/engine/v4/track"
|
|
"m7s.live/engine/v4/util"
|
|
)
|
|
|
|
var defaultFtyp = mp4.NewFtyp("isom", 0x200, []string{
|
|
"isom", "iso2", "avc1", "mp41",
|
|
})
|
|
|
|
type mediaContext struct {
|
|
trackId uint32
|
|
fragment *mp4.Fragment
|
|
ts uint32 // 起始时间戳
|
|
}
|
|
|
|
func (m *mediaContext) push(recoder *Recorder, dt uint32, dur uint32, data []byte, flags uint32) {
|
|
if m.fragment != nil && dt-m.ts > 5000 {
|
|
m.fragment.Encode(recoder.fp)
|
|
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 Recorder struct {
|
|
Subscriber
|
|
fp config.FileWr
|
|
*mp4.InitSegment `json:"-"`
|
|
video mediaContext
|
|
audio mediaContext
|
|
seqNumber uint32
|
|
}
|
|
|
|
func NewRecorder(fp config.FileWr) *Recorder {
|
|
r := &Recorder{
|
|
fp: fp,
|
|
InitSegment: mp4.CreateEmptyInit(),
|
|
}
|
|
r.InitSegment.Ftyp = defaultFtyp
|
|
return r
|
|
}
|
|
|
|
func (r *Recorder) Close() error {
|
|
if r.fp != nil {
|
|
if r.video.fragment != nil {
|
|
r.video.fragment.Encode(r.fp)
|
|
}
|
|
if r.audio.fragment != nil {
|
|
r.audio.fragment.Encode(r.fp)
|
|
}
|
|
r.fp.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Recorder) OnEvent(event any) {
|
|
switch v := event.(type) {
|
|
case *track.Video:
|
|
moov := r.Moov
|
|
trackID := uint32(len(moov.Traks) + 1)
|
|
moov.Mvhd.NextTrackID = trackID + 1
|
|
newTrak := mp4.CreateEmptyTrak(trackID, 1000, "video", "chi")
|
|
moov.AddChild(newTrak)
|
|
moov.Mvex.AddChild(mp4.CreateTrex(trackID))
|
|
r.video.trackId = trackID
|
|
switch v.CodecID {
|
|
case codec.CodecID_H264:
|
|
newTrak.SetAVCDescriptor("avc1", [][]byte{v.DecoderConfiguration.Raw[0]}, [][]byte{v.DecoderConfiguration.Raw[1]})
|
|
case codec.CodecID_H265:
|
|
newTrak.SetHEVCDescriptor("hev1", [][]byte{v.DecoderConfiguration.Raw[0]}, [][]byte{v.DecoderConfiguration.Raw[1]}, [][]byte{v.DecoderConfiguration.Raw[2]})
|
|
}
|
|
r.AddTrack(v)
|
|
case *track.Audio:
|
|
moov := r.Moov
|
|
trackID := uint32(len(moov.Traks) + 1)
|
|
moov.Mvhd.NextTrackID = trackID + 1
|
|
newTrak := mp4.CreateEmptyTrak(trackID, 1000, "audio", "chi")
|
|
moov.AddChild(newTrak)
|
|
moov.Mvex.AddChild(mp4.CreateTrex(trackID))
|
|
r.audio.trackId = trackID
|
|
switch v.CodecID {
|
|
case codec.CodecID_AAC:
|
|
switch v.Profile {
|
|
case 0:
|
|
newTrak.SetAACDescriptor(aac.HEAACv1, int(v.SampleRate))
|
|
case 1:
|
|
newTrak.SetAACDescriptor(aac.AAClc, int(v.SampleRate))
|
|
case 2:
|
|
newTrak.SetAACDescriptor(aac.HEAACv2, int(v.SampleRate))
|
|
}
|
|
case codec.CodecID_PCMA:
|
|
stsd := newTrak.Mdia.Minf.Stbl.Stsd
|
|
pcma := mp4.CreateAudioSampleEntryBox("pcma",
|
|
uint16(v.Channels),
|
|
uint16(v.SampleSize), uint16(v.SampleRate), nil)
|
|
stsd.AddChild(pcma)
|
|
case codec.CodecID_PCMU:
|
|
stsd := newTrak.Mdia.Minf.Stbl.Stsd
|
|
pcmu := mp4.CreateAudioSampleEntryBox("pcmu",
|
|
uint16(v.Channels),
|
|
uint16(v.SampleSize), uint16(v.SampleRate), nil)
|
|
stsd.AddChild(pcmu)
|
|
}
|
|
r.AddTrack(v)
|
|
case *AudioFrame:
|
|
if r.audio.trackId != 0 {
|
|
if r.InitSegment != nil {
|
|
r.InitSegment.Ftyp.Encode(r.fp)
|
|
r.InitSegment.Moov.Encode(r.fp)
|
|
r.InitSegment = nil
|
|
}
|
|
r.audio.push(r, v.AbsTime-r.Audio.First.AbsTime, v.DeltaTime, util.ConcatBuffers(v.Raw), mp4.SyncSampleFlags)
|
|
}
|
|
case *VideoFrame:
|
|
if r.video.trackId != 0 {
|
|
flag := mp4.NonSyncSampleFlags
|
|
if v.IFrame {
|
|
flag = mp4.SyncSampleFlags
|
|
}
|
|
if r.InitSegment != nil {
|
|
r.InitSegment.Ftyp.Encode(r.fp)
|
|
r.InitSegment.Moov.Encode(r.fp)
|
|
r.InitSegment = nil
|
|
}
|
|
r.video.push(r, v.AbsTime-r.Video.First.AbsTime, v.DeltaTime, util.ConcatBuffers(v.AVCC)[5:], flag)
|
|
}
|
|
|
|
default:
|
|
r.Subscriber.OnEvent(event)
|
|
}
|
|
|
|
}
|