diff --git a/config.go b/config.go index 571501e..b9b87d3 100644 --- a/config.go +++ b/config.go @@ -8,6 +8,7 @@ import ( "path/filepath" "regexp" "strings" + "time" "m7s.live/engine/v4" ) @@ -29,7 +30,7 @@ type Record struct { Path string //存储文件的目录 AutoRecord bool Filter string - Fragment int //分片大小(秒)0表示不分片 + Fragment time.Duration //分片大小(秒)0表示不分片 filterReg *regexp.Regexp fs http.Handler CreateFileFn func(filename string, append bool) (FileWr, error) `yaml:"-" json:"-"` diff --git a/default.yaml b/default.yaml index 785acb9..050cd17 100644 --- a/default.yaml +++ b/default.yaml @@ -1,2 +1,2 @@ subscribe: - livemode: false \ No newline at end of file + submode: 1 \ No newline at end of file diff --git a/flv.go b/flv.go index eb859c5..d0673d6 100644 --- a/flv.go +++ b/flv.go @@ -2,7 +2,6 @@ package record import ( "io" - "net" "os" "path/filepath" "strconv" @@ -11,6 +10,7 @@ import ( "go.uber.org/zap" . "m7s.live/engine/v4" "m7s.live/engine/v4/codec" + "m7s.live/engine/v4/util" ) type FLVRecorder struct { @@ -43,10 +43,10 @@ func (r *FLVRecorder) start() { func (r *FLVRecorder) writeMetaData(file *os.File, duration int64) { defer file.Close() - at, vt := r.Audio.Track, r.Video.Track + at, vt := r.Audio, r.Video hasAudio, hasVideo := at != nil, vt != nil - var amf codec.AMF - metaData := codec.EcmaArray{ + var amf util.AMF + metaData := util.EcmaArray{ "MetaDataCreator": "m7s " + Engine.Version, "hasVideo": hasVideo, "hasAudio": hasAudio, @@ -99,7 +99,7 @@ func (r *FLVRecorder) writeMetaData(file *os.File, duration int64) { } else { tempFile.Write([]byte{'F', 'L', 'V', 0x01, flags, 0, 0, 0, 9, 0, 0, 0, 0}) amf.Reset() - codec.WriteFLVTag(tempFile, codec.FLV_TAG_TYPE_SCRIPT, 0, net.Buffers{amf.Marshals("onMetaData", metaData)}) + codec.WriteFLVTag(tempFile, codec.FLV_TAG_TYPE_SCRIPT, 0, amf.Marshals("onMetaData", metaData)) file.Seek(int64(len(codec.FLVHeader)), io.SeekStart) io.Copy(tempFile, file) tempFile.Seek(0, io.SeekStart) @@ -130,18 +130,18 @@ func (r *FLVRecorder) OnEvent(event any) { case FLVFrame: check := false var absTime uint32 - if r.Video.Track == nil { + if r.VideoReader.Track == nil { check = true - absTime = r.Audio.Frame.AbsTime + absTime = r.AudioReader.Frame.AbsTime } else { - check = r.Video.Frame.IFrame - absTime = r.Video.Frame.AbsTime + check = r.VideoReader.Frame.IFrame + absTime = r.VideoReader.Frame.AbsTime if check { r.filepositions = append(r.filepositions, uint64(r.Offset)) r.times = append(r.times, float64(absTime)/1000) } } - if r.Fragment > 0 && check && r.duration >= int64(r.Fragment*1000) { + if r.Fragment > 0 && check && time.Duration(r.duration)*time.Millisecond >= r.Fragment { r.SkipTS = absTime if file, ok := r.Writer.(*os.File); ok { go r.writeMetaData(file, r.duration) @@ -152,15 +152,15 @@ func (r *FLVRecorder) OnEvent(event any) { if file, err := r.CreateFileFn(filepath.Join(r.Stream.Path, strconv.FormatInt(time.Now().Unix(), 10)+r.Ext), false); err == nil { r.SetIO(file) r.Write(codec.FLVHeader) - if r.Video.Track != nil { - dcflv := codec.VideoAVCC2FLV(r.Video.Track.DecoderConfiguration.AVCC, 0) + if r.VideoReader.Track != nil { + dcflv := codec.VideoAVCC2FLV(0, r.VideoReader.Track.SequenceHead) dcflv.WriteTo(r) } - if r.Audio.Track != nil && r.Audio.Track.CodecID == codec.CodecID_AAC { - dcflv := codec.AudioAVCC2FLV(r.Audio.Track.Value.AVCC, 0) + if r.AudioReader.Track != nil && r.Audio.CodecID == codec.CodecID_AAC { + dcflv := codec.AudioAVCC2FLV(0, r.AudioReader.Track.Value.AVCC.ToBuffers()...) dcflv.WriteTo(r) } - flv := codec.VideoAVCC2FLV(r.Video.Frame.AVCC, 0) + flv := codec.VideoAVCC2FLV(0, r.VideoReader.Track.Value.AVCC.ToBuffers()...) flv.WriteTo(r) return } diff --git a/hls.go b/hls.go index bf02b0d..12cc3a7 100644 --- a/hls.go +++ b/hls.go @@ -2,6 +2,7 @@ package record import ( "io" + "math" "path/filepath" "strconv" "time" @@ -15,7 +16,6 @@ import ( type HLSRecorder struct { playlist hls.Playlist - asc *codec.AudioSpecificConfig video_cc, audio_cc byte packet mpegts.MpegTsPESPacket Recorder @@ -46,7 +46,7 @@ func (h *HLSRecorder) OnEvent(event any) { Writer: h.Writer, Version: 3, Sequence: 0, - Targetduration: h.Fragment * 1000, + Targetduration: int(math.Ceil(h.Fragment.Seconds())), } if err = h.playlist.Init(); err != nil { return @@ -55,10 +55,8 @@ func (h *HLSRecorder) OnEvent(event any) { return } go h.start() - case AudioDeConf: - h.asc, err = hls.DecodeAudioSpecificConfig(v.AVCC[0]) - case *AudioFrame: - if h.packet, err = hls.AudioPacketToPES(v, h.asc); err != nil { + case AudioFrame: + if h.packet, err = hls.AudioPacketToPES(&v, &h.Audio.AudioSpecificConfig); err != nil { return } pes := &mpegts.MpegtsPESFrame{ @@ -72,8 +70,8 @@ func (h *HLSRecorder) OnEvent(event any) { return } h.audio_cc = pes.ContinuityCounter - case *VideoFrame: - h.packet, err = hls.VideoPacketToPES(v, h.Video.Track.DecoderConfiguration, h.SkipTS) + case VideoFrame: + h.packet, err = hls.VideoPacketToPES(&v, h.Video) if err != nil { return } @@ -107,7 +105,7 @@ func (h *HLSRecorder) createHlsTsSegmentFile() (err error) { } h.tsWriter = fw inf := hls.PlaylistInf{ - Duration: float64(h.Fragment), + Duration: h.Fragment.Seconds(), Title: tsFilename, } if err = h.playlist.WriteInf(inf); err != nil { @@ -118,11 +116,11 @@ func (h *HLSRecorder) createHlsTsSegmentFile() (err error) { } var vcodec codec.VideoCodecID = 0 var acodec codec.AudioCodecID = 0 - if h.Video.Track != nil { - vcodec = h.Video.Track.CodecID + if h.Video != nil { + vcodec = h.Video.CodecID } - if h.Audio.Track != nil { - acodec = h.Audio.Track.CodecID + if h.Audio != nil { + acodec = h.Audio.CodecID } mpegts.WritePMTPacket(fw, vcodec, acodec) return err diff --git a/mp4.go b/mp4.go index fd55768..6b5b41c 100644 --- a/mp4.go +++ b/mp4.go @@ -10,7 +10,6 @@ import ( . "m7s.live/engine/v4" "m7s.live/engine/v4/codec" "m7s.live/engine/v4/track" - "m7s.live/engine/v4/util" ) type mediaContext struct { @@ -91,11 +90,11 @@ func (r *MP4Recorder) OnEvent(event any) { r.SetIO(file) r.InitSegment = mp4.CreateEmptyInit() r.Moov.Mvhd.NextTrackID = 1 - if r.Video.Track != nil { - r.OnEvent(r.Video.Track) + if r.VideoReader.Track != nil { + r.OnEvent(r.VideoReader.Track) } - if r.Audio.Track != nil { - r.OnEvent(r.Audio.Track) + if r.AudioReader.Track != nil { + r.OnEvent(r.AudioReader.Track) } r.ftyp.Encode(r) r.Moov.Encode(r) @@ -116,12 +115,12 @@ func (r *MP4Recorder) OnEvent(event any) { r.ftyp = mp4.NewFtyp("isom", 0x200, []string{ "isom", "iso2", "avc1", "mp41", }) - newTrak.SetAVCDescriptor("avc1", v.DecoderConfiguration.Raw[0:1], v.DecoderConfiguration.Raw[1:2], true) + newTrak.SetAVCDescriptor("avc1", v.ParamaterSets[0:1], v.ParamaterSets[1:2], true) case codec.CodecID_H265: r.ftyp = mp4.NewFtyp("isom", 0x200, []string{ "isom", "iso2", "hvc1", "mp41", }) - newTrak.SetHEVCDescriptor("hvc1", v.DecoderConfiguration.Raw[0:1], v.DecoderConfiguration.Raw[1:2], v.DecoderConfiguration.Raw[2:3], true) + newTrak.SetHEVCDescriptor("hvc1", v.ParamaterSets[0:1], v.ParamaterSets[1:2], v.ParamaterSets[2:3], true) } r.AddTrack(v) case *track.Audio: @@ -134,12 +133,12 @@ func (r *MP4Recorder) OnEvent(event any) { r.audio.trackId = trackID switch v.CodecID { case codec.CodecID_AAC: - switch v.Profile { - case 0: - newTrak.SetAACDescriptor(aac.HEAACv1, int(v.SampleRate)) + switch v.AudioObjectType { case 1: - newTrak.SetAACDescriptor(aac.AAClc, int(v.SampleRate)) + newTrak.SetAACDescriptor(aac.HEAACv1, int(v.SampleRate)) case 2: + newTrak.SetAACDescriptor(aac.AAClc, int(v.SampleRate)) + case 3: newTrak.SetAACDescriptor(aac.HEAACv2, int(v.SampleRate)) } case codec.CodecID_PCMA: @@ -162,18 +161,18 @@ func (r *MP4Recorder) OnEvent(event any) { r.Moov.Encode(r) go r.start() } - case *AudioFrame: + case AudioFrame: if r.audio.trackId != 0 { - r.audio.push(r, v.AbsTime-r.SkipTS, v.DeltaTime, util.ConcatBuffers(v.Raw), mp4.SyncSampleFlags) + r.audio.push(r, v.AbsTime, v.DeltaTime, v.AUList.ToBytes(), mp4.SyncSampleFlags) } - case *VideoFrame: + case VideoFrame: if r.video.trackId != 0 { flag := mp4.NonSyncSampleFlags if v.IFrame { flag = mp4.SyncSampleFlags } - if data := util.ConcatBuffers(v.AVCC); len(data) > 5 { - r.video.push(r, v.AbsTime-r.SkipTS, v.DeltaTime, data[5:], flag) + if data := v.AUList.ToBytes(); len(data) > 5 { + r.video.push(r, v.AbsTime, v.DeltaTime, data[5:], flag) } } } diff --git a/raw.go b/raw.go index 6a6d02e..84bb940 100644 --- a/raw.go +++ b/raw.go @@ -7,6 +7,7 @@ import ( . "m7s.live/engine/v4" "m7s.live/engine/v4/codec" + "m7s.live/engine/v4/common" "m7s.live/engine/v4/track" ) @@ -36,22 +37,19 @@ func (r *RawRecorder) OnEvent(event any) { r.Ext = ".h265" } } - case VideoDeConf: - annexB := v.GetAnnexB() - annexB.WriteTo(r) - case *VideoFrame: + case common.ParamaterSets: + v.WriteAnnexBTo(r) + case VideoFrame: if r.Fragment != 0 && r.newFile { r.newFile = false r.Close() if file, err := r.CreateFileFn(filepath.Join(r.Stream.Path, strconv.FormatInt(time.Now().Unix(), 10)+r.Ext), false); err == nil { r.SetIO(file) - if r.Video.Track != nil { - annexB := VideoDeConf(r.Video.Track.DecoderConfiguration).GetAnnexB() - annexB.WriteTo(r) + if r.Video != nil { + r.Video.ParamaterSets.WriteAnnexBTo(r) } } } - annexB := v.GetAnnexB() - annexB.WriteTo(r) + v.WriteAnnexBTo(r) } } diff --git a/subscriber.go b/subscriber.go index f271a99..431221e 100644 --- a/subscriber.go +++ b/subscriber.go @@ -10,6 +10,7 @@ import ( type Recorder struct { Subscriber + SkipTS uint32 *Record `json:"-"` newFile bool // 创建了新的文件 append bool // 是否追加模式 @@ -23,7 +24,7 @@ func (r *Recorder) start() { } func (r *Recorder) cut(absTime uint32) { - if ts := absTime - r.SkipTS; int64(ts) >= int64(r.Fragment*1000) { + if ts := absTime - r.SkipTS; time.Duration(ts)*time.Millisecond >= r.Fragment { r.SkipTS = absTime r.newFile = true } @@ -41,12 +42,12 @@ func (r *Recorder) OnEvent(event any) { if file, err := r.CreateFileFn(filename, r.append); err == nil { r.SetIO(file) } - case *AudioFrame: + case AudioFrame: // 纯音频流的情况下需要切割文件 - if r.Fragment > 0 && r.Video.Track == nil { + if r.Fragment > 0 && r.VideoReader.Track == nil { r.cut(v.AbsTime) } - case *VideoFrame: + case VideoFrame: if r.Fragment > 0 && v.IFrame { r.cut(v.AbsTime) }