mirror of
https://github.com/Monibuca/plugin-record.git
synced 2025-10-07 09:41:07 +08:00
适配引擎的升级
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"m7s.live/engine/v4"
|
"m7s.live/engine/v4"
|
||||||
)
|
)
|
||||||
@@ -29,7 +30,7 @@ type Record struct {
|
|||||||
Path string //存储文件的目录
|
Path string //存储文件的目录
|
||||||
AutoRecord bool
|
AutoRecord bool
|
||||||
Filter string
|
Filter string
|
||||||
Fragment int //分片大小(秒)0表示不分片
|
Fragment time.Duration //分片大小(秒)0表示不分片
|
||||||
filterReg *regexp.Regexp
|
filterReg *regexp.Regexp
|
||||||
fs http.Handler
|
fs http.Handler
|
||||||
CreateFileFn func(filename string, append bool) (FileWr, error) `yaml:"-" json:"-"`
|
CreateFileFn func(filename string, append bool) (FileWr, error) `yaml:"-" json:"-"`
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
subscribe:
|
subscribe:
|
||||||
livemode: false
|
submode: 1
|
30
flv.go
30
flv.go
@@ -2,7 +2,6 @@ package record
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -11,6 +10,7 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
. "m7s.live/engine/v4"
|
. "m7s.live/engine/v4"
|
||||||
"m7s.live/engine/v4/codec"
|
"m7s.live/engine/v4/codec"
|
||||||
|
"m7s.live/engine/v4/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FLVRecorder struct {
|
type FLVRecorder struct {
|
||||||
@@ -43,10 +43,10 @@ func (r *FLVRecorder) start() {
|
|||||||
|
|
||||||
func (r *FLVRecorder) writeMetaData(file *os.File, duration int64) {
|
func (r *FLVRecorder) writeMetaData(file *os.File, duration int64) {
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
at, vt := r.Audio.Track, r.Video.Track
|
at, vt := r.Audio, r.Video
|
||||||
hasAudio, hasVideo := at != nil, vt != nil
|
hasAudio, hasVideo := at != nil, vt != nil
|
||||||
var amf codec.AMF
|
var amf util.AMF
|
||||||
metaData := codec.EcmaArray{
|
metaData := util.EcmaArray{
|
||||||
"MetaDataCreator": "m7s " + Engine.Version,
|
"MetaDataCreator": "m7s " + Engine.Version,
|
||||||
"hasVideo": hasVideo,
|
"hasVideo": hasVideo,
|
||||||
"hasAudio": hasAudio,
|
"hasAudio": hasAudio,
|
||||||
@@ -99,7 +99,7 @@ func (r *FLVRecorder) writeMetaData(file *os.File, duration int64) {
|
|||||||
} else {
|
} else {
|
||||||
tempFile.Write([]byte{'F', 'L', 'V', 0x01, flags, 0, 0, 0, 9, 0, 0, 0, 0})
|
tempFile.Write([]byte{'F', 'L', 'V', 0x01, flags, 0, 0, 0, 9, 0, 0, 0, 0})
|
||||||
amf.Reset()
|
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)
|
file.Seek(int64(len(codec.FLVHeader)), io.SeekStart)
|
||||||
io.Copy(tempFile, file)
|
io.Copy(tempFile, file)
|
||||||
tempFile.Seek(0, io.SeekStart)
|
tempFile.Seek(0, io.SeekStart)
|
||||||
@@ -130,18 +130,18 @@ func (r *FLVRecorder) OnEvent(event any) {
|
|||||||
case FLVFrame:
|
case FLVFrame:
|
||||||
check := false
|
check := false
|
||||||
var absTime uint32
|
var absTime uint32
|
||||||
if r.Video.Track == nil {
|
if r.VideoReader.Track == nil {
|
||||||
check = true
|
check = true
|
||||||
absTime = r.Audio.Frame.AbsTime
|
absTime = r.AudioReader.Frame.AbsTime
|
||||||
} else {
|
} else {
|
||||||
check = r.Video.Frame.IFrame
|
check = r.VideoReader.Frame.IFrame
|
||||||
absTime = r.Video.Frame.AbsTime
|
absTime = r.VideoReader.Frame.AbsTime
|
||||||
if check {
|
if check {
|
||||||
r.filepositions = append(r.filepositions, uint64(r.Offset))
|
r.filepositions = append(r.filepositions, uint64(r.Offset))
|
||||||
r.times = append(r.times, float64(absTime)/1000)
|
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
|
r.SkipTS = absTime
|
||||||
if file, ok := r.Writer.(*os.File); ok {
|
if file, ok := r.Writer.(*os.File); ok {
|
||||||
go r.writeMetaData(file, r.duration)
|
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 {
|
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.SetIO(file)
|
||||||
r.Write(codec.FLVHeader)
|
r.Write(codec.FLVHeader)
|
||||||
if r.Video.Track != nil {
|
if r.VideoReader.Track != nil {
|
||||||
dcflv := codec.VideoAVCC2FLV(r.Video.Track.DecoderConfiguration.AVCC, 0)
|
dcflv := codec.VideoAVCC2FLV(0, r.VideoReader.Track.SequenceHead)
|
||||||
dcflv.WriteTo(r)
|
dcflv.WriteTo(r)
|
||||||
}
|
}
|
||||||
if r.Audio.Track != nil && r.Audio.Track.CodecID == codec.CodecID_AAC {
|
if r.AudioReader.Track != nil && r.Audio.CodecID == codec.CodecID_AAC {
|
||||||
dcflv := codec.AudioAVCC2FLV(r.Audio.Track.Value.AVCC, 0)
|
dcflv := codec.AudioAVCC2FLV(0, r.AudioReader.Track.Value.AVCC.ToBuffers()...)
|
||||||
dcflv.WriteTo(r)
|
dcflv.WriteTo(r)
|
||||||
}
|
}
|
||||||
flv := codec.VideoAVCC2FLV(r.Video.Frame.AVCC, 0)
|
flv := codec.VideoAVCC2FLV(0, r.VideoReader.Track.Value.AVCC.ToBuffers()...)
|
||||||
flv.WriteTo(r)
|
flv.WriteTo(r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
24
hls.go
24
hls.go
@@ -2,6 +2,7 @@ package record
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@@ -15,7 +16,6 @@ import (
|
|||||||
|
|
||||||
type HLSRecorder struct {
|
type HLSRecorder struct {
|
||||||
playlist hls.Playlist
|
playlist hls.Playlist
|
||||||
asc *codec.AudioSpecificConfig
|
|
||||||
video_cc, audio_cc byte
|
video_cc, audio_cc byte
|
||||||
packet mpegts.MpegTsPESPacket
|
packet mpegts.MpegTsPESPacket
|
||||||
Recorder
|
Recorder
|
||||||
@@ -46,7 +46,7 @@ func (h *HLSRecorder) OnEvent(event any) {
|
|||||||
Writer: h.Writer,
|
Writer: h.Writer,
|
||||||
Version: 3,
|
Version: 3,
|
||||||
Sequence: 0,
|
Sequence: 0,
|
||||||
Targetduration: h.Fragment * 1000,
|
Targetduration: int(math.Ceil(h.Fragment.Seconds())),
|
||||||
}
|
}
|
||||||
if err = h.playlist.Init(); err != nil {
|
if err = h.playlist.Init(); err != nil {
|
||||||
return
|
return
|
||||||
@@ -55,10 +55,8 @@ func (h *HLSRecorder) OnEvent(event any) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
go h.start()
|
go h.start()
|
||||||
case AudioDeConf:
|
case AudioFrame:
|
||||||
h.asc, err = hls.DecodeAudioSpecificConfig(v.AVCC[0])
|
if h.packet, err = hls.AudioPacketToPES(&v, &h.Audio.AudioSpecificConfig); err != nil {
|
||||||
case *AudioFrame:
|
|
||||||
if h.packet, err = hls.AudioPacketToPES(v, h.asc); err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pes := &mpegts.MpegtsPESFrame{
|
pes := &mpegts.MpegtsPESFrame{
|
||||||
@@ -72,8 +70,8 @@ func (h *HLSRecorder) OnEvent(event any) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.audio_cc = pes.ContinuityCounter
|
h.audio_cc = pes.ContinuityCounter
|
||||||
case *VideoFrame:
|
case VideoFrame:
|
||||||
h.packet, err = hls.VideoPacketToPES(v, h.Video.Track.DecoderConfiguration, h.SkipTS)
|
h.packet, err = hls.VideoPacketToPES(&v, h.Video)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -107,7 +105,7 @@ func (h *HLSRecorder) createHlsTsSegmentFile() (err error) {
|
|||||||
}
|
}
|
||||||
h.tsWriter = fw
|
h.tsWriter = fw
|
||||||
inf := hls.PlaylistInf{
|
inf := hls.PlaylistInf{
|
||||||
Duration: float64(h.Fragment),
|
Duration: h.Fragment.Seconds(),
|
||||||
Title: tsFilename,
|
Title: tsFilename,
|
||||||
}
|
}
|
||||||
if err = h.playlist.WriteInf(inf); err != nil {
|
if err = h.playlist.WriteInf(inf); err != nil {
|
||||||
@@ -118,11 +116,11 @@ func (h *HLSRecorder) createHlsTsSegmentFile() (err error) {
|
|||||||
}
|
}
|
||||||
var vcodec codec.VideoCodecID = 0
|
var vcodec codec.VideoCodecID = 0
|
||||||
var acodec codec.AudioCodecID = 0
|
var acodec codec.AudioCodecID = 0
|
||||||
if h.Video.Track != nil {
|
if h.Video != nil {
|
||||||
vcodec = h.Video.Track.CodecID
|
vcodec = h.Video.CodecID
|
||||||
}
|
}
|
||||||
if h.Audio.Track != nil {
|
if h.Audio != nil {
|
||||||
acodec = h.Audio.Track.CodecID
|
acodec = h.Audio.CodecID
|
||||||
}
|
}
|
||||||
mpegts.WritePMTPacket(fw, vcodec, acodec)
|
mpegts.WritePMTPacket(fw, vcodec, acodec)
|
||||||
return err
|
return err
|
||||||
|
31
mp4.go
31
mp4.go
@@ -10,7 +10,6 @@ import (
|
|||||||
. "m7s.live/engine/v4"
|
. "m7s.live/engine/v4"
|
||||||
"m7s.live/engine/v4/codec"
|
"m7s.live/engine/v4/codec"
|
||||||
"m7s.live/engine/v4/track"
|
"m7s.live/engine/v4/track"
|
||||||
"m7s.live/engine/v4/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type mediaContext struct {
|
type mediaContext struct {
|
||||||
@@ -91,11 +90,11 @@ func (r *MP4Recorder) OnEvent(event any) {
|
|||||||
r.SetIO(file)
|
r.SetIO(file)
|
||||||
r.InitSegment = mp4.CreateEmptyInit()
|
r.InitSegment = mp4.CreateEmptyInit()
|
||||||
r.Moov.Mvhd.NextTrackID = 1
|
r.Moov.Mvhd.NextTrackID = 1
|
||||||
if r.Video.Track != nil {
|
if r.VideoReader.Track != nil {
|
||||||
r.OnEvent(r.Video.Track)
|
r.OnEvent(r.VideoReader.Track)
|
||||||
}
|
}
|
||||||
if r.Audio.Track != nil {
|
if r.AudioReader.Track != nil {
|
||||||
r.OnEvent(r.Audio.Track)
|
r.OnEvent(r.AudioReader.Track)
|
||||||
}
|
}
|
||||||
r.ftyp.Encode(r)
|
r.ftyp.Encode(r)
|
||||||
r.Moov.Encode(r)
|
r.Moov.Encode(r)
|
||||||
@@ -116,12 +115,12 @@ func (r *MP4Recorder) OnEvent(event any) {
|
|||||||
r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
|
r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
|
||||||
"isom", "iso2", "avc1", "mp41",
|
"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:
|
case codec.CodecID_H265:
|
||||||
r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
|
r.ftyp = mp4.NewFtyp("isom", 0x200, []string{
|
||||||
"isom", "iso2", "hvc1", "mp41",
|
"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)
|
r.AddTrack(v)
|
||||||
case *track.Audio:
|
case *track.Audio:
|
||||||
@@ -134,12 +133,12 @@ func (r *MP4Recorder) OnEvent(event any) {
|
|||||||
r.audio.trackId = trackID
|
r.audio.trackId = trackID
|
||||||
switch v.CodecID {
|
switch v.CodecID {
|
||||||
case codec.CodecID_AAC:
|
case codec.CodecID_AAC:
|
||||||
switch v.Profile {
|
switch v.AudioObjectType {
|
||||||
case 0:
|
|
||||||
newTrak.SetAACDescriptor(aac.HEAACv1, int(v.SampleRate))
|
|
||||||
case 1:
|
case 1:
|
||||||
newTrak.SetAACDescriptor(aac.AAClc, int(v.SampleRate))
|
newTrak.SetAACDescriptor(aac.HEAACv1, int(v.SampleRate))
|
||||||
case 2:
|
case 2:
|
||||||
|
newTrak.SetAACDescriptor(aac.AAClc, int(v.SampleRate))
|
||||||
|
case 3:
|
||||||
newTrak.SetAACDescriptor(aac.HEAACv2, int(v.SampleRate))
|
newTrak.SetAACDescriptor(aac.HEAACv2, int(v.SampleRate))
|
||||||
}
|
}
|
||||||
case codec.CodecID_PCMA:
|
case codec.CodecID_PCMA:
|
||||||
@@ -162,18 +161,18 @@ func (r *MP4Recorder) OnEvent(event any) {
|
|||||||
r.Moov.Encode(r)
|
r.Moov.Encode(r)
|
||||||
go r.start()
|
go r.start()
|
||||||
}
|
}
|
||||||
case *AudioFrame:
|
case AudioFrame:
|
||||||
if r.audio.trackId != 0 {
|
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 {
|
if r.video.trackId != 0 {
|
||||||
flag := mp4.NonSyncSampleFlags
|
flag := mp4.NonSyncSampleFlags
|
||||||
if v.IFrame {
|
if v.IFrame {
|
||||||
flag = mp4.SyncSampleFlags
|
flag = mp4.SyncSampleFlags
|
||||||
}
|
}
|
||||||
if data := util.ConcatBuffers(v.AVCC); len(data) > 5 {
|
if data := v.AUList.ToBytes(); len(data) > 5 {
|
||||||
r.video.push(r, v.AbsTime-r.SkipTS, v.DeltaTime, data[5:], flag)
|
r.video.push(r, v.AbsTime, v.DeltaTime, data[5:], flag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
raw.go
16
raw.go
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
. "m7s.live/engine/v4"
|
. "m7s.live/engine/v4"
|
||||||
"m7s.live/engine/v4/codec"
|
"m7s.live/engine/v4/codec"
|
||||||
|
"m7s.live/engine/v4/common"
|
||||||
"m7s.live/engine/v4/track"
|
"m7s.live/engine/v4/track"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,22 +37,19 @@ func (r *RawRecorder) OnEvent(event any) {
|
|||||||
r.Ext = ".h265"
|
r.Ext = ".h265"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case VideoDeConf:
|
case common.ParamaterSets:
|
||||||
annexB := v.GetAnnexB()
|
v.WriteAnnexBTo(r)
|
||||||
annexB.WriteTo(r)
|
case VideoFrame:
|
||||||
case *VideoFrame:
|
|
||||||
if r.Fragment != 0 && r.newFile {
|
if r.Fragment != 0 && r.newFile {
|
||||||
r.newFile = false
|
r.newFile = false
|
||||||
r.Close()
|
r.Close()
|
||||||
if file, err := r.CreateFileFn(filepath.Join(r.Stream.Path, strconv.FormatInt(time.Now().Unix(), 10)+r.Ext), false); err == nil {
|
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.SetIO(file)
|
||||||
if r.Video.Track != nil {
|
if r.Video != nil {
|
||||||
annexB := VideoDeConf(r.Video.Track.DecoderConfiguration).GetAnnexB()
|
r.Video.ParamaterSets.WriteAnnexBTo(r)
|
||||||
annexB.WriteTo(r)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
annexB := v.GetAnnexB()
|
v.WriteAnnexBTo(r)
|
||||||
annexB.WriteTo(r)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
type Recorder struct {
|
type Recorder struct {
|
||||||
Subscriber
|
Subscriber
|
||||||
|
SkipTS uint32
|
||||||
*Record `json:"-"`
|
*Record `json:"-"`
|
||||||
newFile bool // 创建了新的文件
|
newFile bool // 创建了新的文件
|
||||||
append bool // 是否追加模式
|
append bool // 是否追加模式
|
||||||
@@ -23,7 +24,7 @@ func (r *Recorder) start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Recorder) cut(absTime uint32) {
|
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.SkipTS = absTime
|
||||||
r.newFile = true
|
r.newFile = true
|
||||||
}
|
}
|
||||||
@@ -41,12 +42,12 @@ func (r *Recorder) OnEvent(event any) {
|
|||||||
if file, err := r.CreateFileFn(filename, r.append); err == nil {
|
if file, err := r.CreateFileFn(filename, r.append); err == nil {
|
||||||
r.SetIO(file)
|
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)
|
r.cut(v.AbsTime)
|
||||||
}
|
}
|
||||||
case *VideoFrame:
|
case VideoFrame:
|
||||||
if r.Fragment > 0 && v.IFrame {
|
if r.Fragment > 0 && v.IFrame {
|
||||||
r.cut(v.AbsTime)
|
r.cut(v.AbsTime)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user