mirror of
				https://github.com/Monibuca/plugin-record.git
				synced 2025-10-31 11:56:25 +08:00 
			
		
		
		
	feat: support mp4 (not fmp4) format, start record can override fragment config,add fileName args
This commit is contained in:
		| @@ -61,7 +61,7 @@ record: | |||||||
|  |  | ||||||
| - `/record/api/list/recording` 罗列所有正在录制中的流的信息 | - `/record/api/list/recording` 罗列所有正在录制中的流的信息 | ||||||
| - `/record/api/list?type=flv` 罗列所有录制的flv文件 | - `/record/api/list?type=flv` 罗列所有录制的flv文件 | ||||||
| - `/record/api/start?type=flv&streamPath=live/rtc` 开始录制某个流,返回一个字符串用于停止录制用的id | - `/record/api/start?type=flv&streamPath=live/rtc&fileName=xxx` 开始录制某个流,返回一个字符串用于停止录制用的id(fileName是可选的,且只用于非切片情况) | ||||||
| - `/record/api/stop?id=xxx` 停止录制某个流 | - `/record/api/stop?id=xxx` 停止录制某个流 | ||||||
|  |  | ||||||
| 其中将type值改为mp4则录制成fmp4格式。 | 其中将type值改为mp4则录制成fmp4格式。 | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								config.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								config.go
									
									
									
									
									
								
							| @@ -10,7 +10,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"m7s.live/engine/v4" | 	"m7s.live/engine/v4/util" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type FileWr interface { | type FileWr interface { | ||||||
| @@ -35,7 +35,7 @@ type Record struct { | |||||||
| 	fs            http.Handler | 	fs            http.Handler | ||||||
| 	CreateFileFn  func(filename string, append bool) (FileWr, error) `json:"-" yaml:"-"` | 	CreateFileFn  func(filename string, append bool) (FileWr, error) `json:"-" yaml:"-"` | ||||||
| 	GetDurationFn func(file io.ReadSeeker) uint32                    `json:"-" yaml:"-"` | 	GetDurationFn func(file io.ReadSeeker) uint32                    `json:"-" yaml:"-"` | ||||||
| 	recording     map[string]engine.ISubscriber | 	recording     map[string]IRecorder | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *Record) ServeHTTP(w http.ResponseWriter, req *http.Request) { | func (r *Record) ServeHTTP(w http.ResponseWriter, req *http.Request) { | ||||||
| @@ -43,14 +43,11 @@ func (r *Record) ServeHTTP(w http.ResponseWriter, req *http.Request) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (r *Record) NeedRecord(streamPath string) bool { | func (r *Record) NeedRecord(streamPath string) bool { | ||||||
| 	if _, ok := r.recording[streamPath]; ok { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	return r.AutoRecord && (r.filterReg == nil || r.filterReg.MatchString(streamPath)) | 	return r.AutoRecord && (r.filterReg == nil || r.filterReg.MatchString(streamPath)) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *Record) Init() { | func (r *Record) Init() { | ||||||
| 	r.recording = make(map[string]engine.ISubscriber) | 	r.recording = make(map[string]IRecorder) | ||||||
| 	os.MkdirAll(r.Path, 0766) | 	os.MkdirAll(r.Path, 0766) | ||||||
| 	if r.Filter != "" { | 	if r.Filter != "" { | ||||||
| 		r.filterReg = regexp.MustCompile(r.Filter) | 		r.filterReg = regexp.MustCompile(r.Filter) | ||||||
| @@ -58,16 +55,10 @@ func (r *Record) Init() { | |||||||
| 	r.fs = http.FileServer(http.Dir(r.Path)) | 	r.fs = http.FileServer(http.Dir(r.Path)) | ||||||
| 	r.CreateFileFn = func(filename string, append bool) (file FileWr, err error) { | 	r.CreateFileFn = func(filename string, append bool) (file FileWr, err error) { | ||||||
| 		filePath := filepath.Join(r.Path, filename) | 		filePath := filepath.Join(r.Path, filename) | ||||||
| 		flag := os.O_CREATE |  | ||||||
| 		if append { |  | ||||||
| 			flag = flag | os.O_RDWR | os.O_APPEND |  | ||||||
| 		} else { |  | ||||||
| 			flag = flag | os.O_RDWR | os.O_TRUNC |  | ||||||
| 		} |  | ||||||
| 		if err = os.MkdirAll(filepath.Dir(filePath), 0766); err != nil { | 		if err = os.MkdirAll(filepath.Dir(filePath), 0766); err != nil { | ||||||
| 			return file, err | 			return file, err | ||||||
| 		} | 		} | ||||||
| 		file, err = os.OpenFile(filePath, flag, 0766) | 		file, err = os.OpenFile(filePath, os.O_CREATE | os.O_RDWR | util.Conditoinal(append, os.O_APPEND, os.O_TRUNC), 0766) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										111
									
								
								flv.go
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								flv.go
									
									
									
									
									
								
							| @@ -4,8 +4,6 @@ import ( | |||||||
| 	"io" | 	"io" | ||||||
| 	"net" | 	"net" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" |  | ||||||
| 	"strconv" |  | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"go.uber.org/zap" | 	"go.uber.org/zap" | ||||||
| @@ -22,22 +20,18 @@ type FLVRecorder struct { | |||||||
| 	duration      int64 | 	duration      int64 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewFLVRecorder() (r *FLVRecorder) { | ||||||
|  | 	r = &FLVRecorder{} | ||||||
|  | 	r.Record = RecordPluginConfig.Flv | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
| func (r *FLVRecorder) Start(streamPath string) (err error) { | func (r *FLVRecorder) Start(streamPath string) (err error) { | ||||||
| 	r.Record = &RecordPluginConfig.Flv |  | ||||||
| 	r.ID = streamPath + "/flv" | 	r.ID = streamPath + "/flv" | ||||||
| 	if _, ok := RecordPluginConfig.recordings.Load(r.ID); ok { | 	return r.start(r, streamPath, SUBTYPE_FLV) | ||||||
| 		return ErrRecordExist |  | ||||||
| 	} |  | ||||||
| 	return plugin.Subscribe(streamPath, r) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *FLVRecorder) start() { | func (r *FLVRecorder) writeMetaData(file FileWr, duration int64) { | ||||||
| 	RecordPluginConfig.recordings.Store(r.ID, r) |  | ||||||
| 	r.PlayFLV() |  | ||||||
| 	RecordPluginConfig.recordings.Delete(r.ID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *FLVRecorder) writeMetaData(file *os.File, duration int64) { |  | ||||||
| 	defer file.Close() | 	defer file.Close() | ||||||
| 	at, vt := r.Audio, r.Video | 	at, vt := r.Audio, r.Video | ||||||
| 	hasAudio, hasVideo := at != nil, vt != nil | 	hasAudio, hasVideo := at != nil, vt != nil | ||||||
| @@ -127,46 +121,34 @@ func (r *FLVRecorder) writeMetaData(file *os.File, duration int64) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (r *FLVRecorder) OnEvent(event any) { | func (r *FLVRecorder) OnEvent(event any) { | ||||||
|  | 	r.Recorder.OnEvent(event) | ||||||
| 	switch v := event.(type) { | 	switch v := event.(type) { | ||||||
| 	case ISubscriber: | 	case FileWr: | ||||||
| 		filename := strconv.FormatInt(time.Now().Unix(), 10) + r.Ext | 		// 写入文件头 | ||||||
| 		if r.Fragment == 0 { | 		if !r.append { | ||||||
| 			filename = r.Stream.Path + r.Ext | 			v.Write(codec.FLVHeader) | ||||||
| 		} else { | 		} else { | ||||||
| 			filename = filepath.Join(r.Stream.Path, filename) | 			if _, err := v.Seek(-4, io.SeekEnd); err != nil { | ||||||
| 		} | 				r.Error("seek file failed", zap.Error(err)) | ||||||
| 		if file, err := r.CreateFileFn(filename, r.append); err == nil { | 				v.Write(codec.FLVHeader) | ||||||
| 			r.SetIO(file) |  | ||||||
| 			// 写入文件头 |  | ||||||
| 			if !r.append { |  | ||||||
| 				r.Write(codec.FLVHeader) |  | ||||||
| 			} else { | 			} else { | ||||||
| 				if _, err = file.Seek(-4, io.SeekEnd); err != nil { | 				tmp := make(util.Buffer, 4) | ||||||
| 					r.Error("seek file failed", zap.Error(err)) | 				tmp2 := tmp | ||||||
| 					r.Write(codec.FLVHeader) | 				v.Read(tmp) | ||||||
| 				} else { | 				tagSize := tmp.ReadUint32() | ||||||
| 					tmp := make(util.Buffer, 4) | 				tmp = tmp2 | ||||||
| 					tmp2 := tmp | 				v.Seek(int64(tagSize), io.SeekEnd) | ||||||
| 					file.Read(tmp) | 				v.Read(tmp2) | ||||||
| 					tagSize := tmp.ReadUint32() | 				ts := tmp2.ReadUint24() | (uint32(tmp[3]) << 24) | ||||||
| 					tmp = tmp2 | 				r.Info("append flv", zap.Uint32("last tagSize", tagSize), zap.Uint32("last ts", ts)) | ||||||
| 					file.Seek(int64(tagSize), io.SeekEnd) | 				if r.VideoReader != nil { | ||||||
| 					file.Read(tmp2) | 					r.VideoReader.StartTs = time.Duration(ts) * time.Millisecond | ||||||
| 					ts := tmp2.ReadUint24() | (uint32(tmp[3]) << 24) |  | ||||||
| 					r.Info("append flv", zap.String("filename", filename), zap.Uint32("last tagSize", tagSize), zap.Uint32("last ts", ts)) |  | ||||||
| 					if r.VideoReader != nil { |  | ||||||
| 						r.VideoReader.StartTs = time.Duration(ts) * time.Millisecond |  | ||||||
| 					} |  | ||||||
| 					if r.AudioReader != nil { |  | ||||||
| 						r.AudioReader.StartTs = time.Duration(ts) * time.Millisecond |  | ||||||
| 					} |  | ||||||
| 					file.Seek(0, io.SeekEnd) |  | ||||||
| 				} | 				} | ||||||
|  | 				if r.AudioReader != nil { | ||||||
|  | 					r.AudioReader.StartTs = time.Duration(ts) * time.Millisecond | ||||||
|  | 				} | ||||||
|  | 				v.Seek(0, io.SeekEnd) | ||||||
| 			} | 			} | ||||||
| 			go r.start() |  | ||||||
| 		} else { |  | ||||||
| 			r.Error("create file failed", zap.Error(err)) |  | ||||||
| 			r.Stop() |  | ||||||
| 		} | 		} | ||||||
| 	case FLVFrame: | 	case FLVFrame: | ||||||
| 		check := false | 		check := false | ||||||
| @@ -186,15 +168,15 @@ func (r *FLVRecorder) OnEvent(event any) { | |||||||
| 		if r.duration = int64(absTime); r.Fragment > 0 && check && time.Duration(r.duration)*time.Millisecond >= r.Fragment { | 		if r.duration = int64(absTime); r.Fragment > 0 && check && time.Duration(r.duration)*time.Millisecond >= r.Fragment { | ||||||
| 			r.Close() | 			r.Close() | ||||||
| 			r.Offset = 0 | 			r.Offset = 0 | ||||||
| 			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.createFile(); err == nil { | ||||||
| 				r.SetIO(file) | 				r.File = file | ||||||
| 				r.Write(codec.FLVHeader) | 				file.Write(codec.FLVHeader) | ||||||
| 				var dcflv net.Buffers | 				var dcflv net.Buffers | ||||||
| 				if r.VideoReader != nil { | 				if r.VideoReader != nil { | ||||||
| 					r.VideoReader.ResetAbsTime() | 					r.VideoReader.ResetAbsTime() | ||||||
| 					dcflv = codec.VideoAVCC2FLV(0, r.VideoReader.Track.SequenceHead) | 					dcflv = codec.VideoAVCC2FLV(0, r.VideoReader.Track.SequenceHead) | ||||||
| 					flv := append(dcflv, codec.VideoAVCC2FLV(0, r.VideoReader.Value.AVCC.ToBuffers()...)...) | 					flv := append(dcflv, codec.VideoAVCC2FLV(0, r.VideoReader.Value.AVCC.ToBuffers()...)...) | ||||||
| 					flv.WriteTo(r) | 					flv.WriteTo(file) | ||||||
| 				} | 				} | ||||||
| 				if r.AudioReader != nil { | 				if r.AudioReader != nil { | ||||||
| 					r.AudioReader.ResetAbsTime() | 					r.AudioReader.ResetAbsTime() | ||||||
| @@ -202,32 +184,27 @@ func (r *FLVRecorder) OnEvent(event any) { | |||||||
| 						dcflv = codec.AudioAVCC2FLV(0, r.AudioReader.Track.SequenceHead) | 						dcflv = codec.AudioAVCC2FLV(0, r.AudioReader.Track.SequenceHead) | ||||||
| 					} | 					} | ||||||
| 					flv := append(dcflv, codec.AudioAVCC2FLV(0, r.AudioReader.Value.AVCC.ToBuffers()...)...) | 					flv := append(dcflv, codec.AudioAVCC2FLV(0, r.AudioReader.Value.AVCC.ToBuffers()...)...) | ||||||
| 					flv.WriteTo(r) | 					flv.WriteTo(file) | ||||||
| 				} | 				} | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if n, err := v.WriteTo(r); err != nil { | 		if n, err := v.WriteTo(r.File); err != nil { | ||||||
| 			r.Error("write file failed", zap.Error(err)) | 			r.Error("write file failed", zap.Error(err)) | ||||||
| 			r.Stop() | 			r.Stop(zap.Error(err)) | ||||||
| 		} else { | 		} else { | ||||||
| 			r.Offset += n | 			r.Offset += n | ||||||
| 		} | 		} | ||||||
| 	default: |  | ||||||
| 		r.Subscriber.OnEvent(event) |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *FLVRecorder) SetIO(file any) { |  | ||||||
| 	r.Subscriber.SetIO(file) |  | ||||||
| 	r.Closer = r |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *FLVRecorder) Close() error { | func (r *FLVRecorder) Close() error { | ||||||
| 	if file, ok := r.Writer.(*os.File); ok && !r.append { | 	if r.File != nil { | ||||||
| 		go r.writeMetaData(file, r.duration) | 		if !r.append { | ||||||
| 	} else if closer, ok := r.Writer.(io.Closer); ok { | 			go r.writeMetaData(r.File, r.duration) | ||||||
| 		return closer.Close() | 		} else { | ||||||
|  | 			return r.File.Close() | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										149
									
								
								fmp4.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								fmp4.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | |||||||
|  | package record | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/edgeware/mp4ff/aac" | ||||||
|  | 	"github.com/edgeware/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) | ||||||
|  | 		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 | ||||||
|  | 	*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.Moov.Mvhd.NextTrackID = 1 | ||||||
|  | 		if r.VideoReader != nil { | ||||||
|  | 			moov := r.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], true) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if r.AudioReader != nil { | ||||||
|  | 			moov := r.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) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		r.ftyp.Encode(r) | ||||||
|  | 		r.Moov.Encode(r) | ||||||
|  | 		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) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								go.mod
									
									
									
									
									
								
							| @@ -4,13 +4,19 @@ go 1.19 | |||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/edgeware/mp4ff v0.28.0 | 	github.com/edgeware/mp4ff v0.28.0 | ||||||
| 	go.uber.org/zap v1.23.0 | 	github.com/yapingcat/gomedia v0.0.0-20230727105416-c491e66c9d2a | ||||||
| 	m7s.live/engine/v4 v4.12.6 | 	go.uber.org/zap v1.24.0 | ||||||
| 	m7s.live/plugin/hls/v4 v4.0.0-20220619163635-447976e65ab9 | 	m7s.live/engine/v4 v4.13.8 | ||||||
|  | 	m7s.live/plugin/hls/v4 v4.3.2 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/aler9/gortsplib/v2 v2.2.2 // indirect | 	github.com/abema/go-mp4 v0.10.1 // indirect | ||||||
|  | 	github.com/aler9/writerseeker v0.0.0-20220601075008-6f0e685b9c82 // indirect | ||||||
|  | 	github.com/asticode/go-astikit v0.30.0 // indirect | ||||||
|  | 	github.com/asticode/go-astits v1.11.0 // indirect | ||||||
|  | 	github.com/bluenviron/gohlslib v0.2.5 // indirect | ||||||
|  | 	github.com/bluenviron/mediacommon v0.7.0 // indirect | ||||||
| 	github.com/cnotch/ipchub v1.1.0 // indirect | 	github.com/cnotch/ipchub v1.1.0 // indirect | ||||||
| 	github.com/denisbrodbeck/machineid v1.0.1 // indirect | 	github.com/denisbrodbeck/machineid v1.0.1 // indirect | ||||||
| 	github.com/go-ole/go-ole v1.2.6 // indirect | 	github.com/go-ole/go-ole v1.2.6 // indirect | ||||||
| @@ -23,8 +29,8 @@ require ( | |||||||
| 	github.com/mcuadros/go-defaults v1.2.0 // indirect | 	github.com/mcuadros/go-defaults v1.2.0 // indirect | ||||||
| 	github.com/onsi/ginkgo/v2 v2.2.0 // indirect | 	github.com/onsi/ginkgo/v2 v2.2.0 // indirect | ||||||
| 	github.com/pion/randutil v0.1.0 // indirect | 	github.com/pion/randutil v0.1.0 // indirect | ||||||
| 	github.com/pion/rtp v1.7.13 // indirect | 	github.com/pion/rtp v1.8.0 // indirect | ||||||
| 	github.com/pion/webrtc/v3 v3.1.49 // indirect | 	github.com/pion/webrtc/v3 v3.1.56 // indirect | ||||||
| 	github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect | 	github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect | ||||||
| 	github.com/q191201771/naza v0.30.8 // indirect | 	github.com/q191201771/naza v0.30.8 // indirect | ||||||
| 	github.com/quangngotan95/go-m3u8 v0.1.0 // indirect | 	github.com/quangngotan95/go-m3u8 v0.1.0 // indirect | ||||||
| @@ -32,19 +38,18 @@ require ( | |||||||
| 	github.com/quic-go/qtls-go1-19 v0.2.0 // indirect | 	github.com/quic-go/qtls-go1-19 v0.2.0 // indirect | ||||||
| 	github.com/quic-go/qtls-go1-20 v0.1.0 // indirect | 	github.com/quic-go/qtls-go1-20 v0.1.0 // indirect | ||||||
| 	github.com/quic-go/quic-go v0.32.0 // indirect | 	github.com/quic-go/quic-go v0.32.0 // indirect | ||||||
| 	github.com/shirou/gopsutil/v3 v3.22.10 // indirect | 	github.com/shirou/gopsutil/v3 v3.22.11 // indirect | ||||||
| 	github.com/tklauser/go-sysconf v0.3.11 // indirect | 	github.com/tklauser/go-sysconf v0.3.11 // indirect | ||||||
| 	github.com/tklauser/numcpus v0.6.0 // indirect | 	github.com/tklauser/numcpus v0.6.0 // indirect | ||||||
| 	github.com/yapingcat/gomedia v0.0.0-20230426092936-387031404274 // indirect |  | ||||||
| 	github.com/yusufpapurcu/wmi v1.2.2 // indirect | 	github.com/yusufpapurcu/wmi v1.2.2 // indirect | ||||||
| 	go.uber.org/atomic v1.10.0 // indirect | 	go.uber.org/atomic v1.10.0 // indirect | ||||||
| 	go.uber.org/multierr v1.8.0 // indirect | 	go.uber.org/multierr v1.8.0 // indirect | ||||||
| 	golang.org/x/crypto v0.4.0 // indirect | 	golang.org/x/crypto v0.11.0 // indirect | ||||||
| 	golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect | 	golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect | ||||||
| 	golang.org/x/mod v0.7.0 // indirect | 	golang.org/x/mod v0.7.0 // indirect | ||||||
| 	golang.org/x/net v0.8.0 // indirect | 	golang.org/x/net v0.12.0 // indirect | ||||||
| 	golang.org/x/sync v0.1.0 // indirect | 	golang.org/x/sync v0.1.0 // indirect | ||||||
| 	golang.org/x/sys v0.6.0 // indirect | 	golang.org/x/sys v0.10.0 // indirect | ||||||
| 	golang.org/x/tools v0.3.0 // indirect | 	golang.org/x/tools v0.3.0 // indirect | ||||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										145
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,7 +1,19 @@ | |||||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||||
| github.com/aler9/gortsplib/v2 v2.2.2 h1:tTw8pdKSOEjlZjjE1S4ftXPHJkYOqjNNv3hjQ0Nto9M= | github.com/abema/go-mp4 v0.10.1 h1:wOhZgNxjduc8r4FJdwPa5x/gdBSSX+8MTnfNj/xkJaE= | ||||||
| github.com/aler9/gortsplib/v2 v2.2.2/go.mod h1:k6uBVHGwsIc/0L5SLLqWwi6bSJUb4VR0HfvncyHlKQI= | github.com/abema/go-mp4 v0.10.1/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws= | ||||||
|  | github.com/aler9/writerseeker v0.0.0-20220601075008-6f0e685b9c82 h1:9WgSzBLo3a9ToSVV7sRTBYZ1GGOZUpq4+5H3SN0UZq4= | ||||||
|  | github.com/aler9/writerseeker v0.0.0-20220601075008-6f0e685b9c82/go.mod h1:qsMrZCbeBf/mCLOeF16KDkPu4gktn/pOWyaq1aYQE7U= | ||||||
|  | github.com/asticode/go-astikit v0.30.0 h1:DkBkRQRIxYcknlaU7W7ksNfn4gMFsB0tqMJflxkRsZA= | ||||||
|  | github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= | ||||||
|  | github.com/asticode/go-astits v1.11.0 h1:GTHUXht0ZXAJXsVbsLIcyfHr1Bchi4QQwMARw2ZWAng= | ||||||
|  | github.com/asticode/go-astits v1.11.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI= | ||||||
| github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= | ||||||
|  | github.com/bluenviron/gohlslib v0.2.5 h1:M+uWOLjOa7hirUITLndf9bYarA8F7HR1m7+5dG/3WVI= | ||||||
|  | github.com/bluenviron/gohlslib v0.2.5/go.mod h1:IvhV5h+92FljHy/xHlcOZm09rDeqg1RR88MjCTBx67Y= | ||||||
|  | github.com/bluenviron/mediacommon v0.7.0 h1:dJWLLL9oDbAqfK8KuNfnDUQwNbeMAtGeRjZc9Vo95js= | ||||||
|  | github.com/bluenviron/mediacommon v0.7.0/go.mod h1:wuLJdxcITiSPgY1MvQqrX+qPlKmNfeV9wNvXth5M98I= | ||||||
|  | github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= | ||||||
|  | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= | ||||||
| github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | ||||||
| github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | ||||||
| github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | ||||||
| @@ -13,6 +25,7 @@ github.com/cnotch/queue v0.0.0-20200326024423-6e88bdbf2ad4/go.mod h1:zOssjAlNusO | |||||||
| github.com/cnotch/queue v0.0.0-20201224060551-4191569ce8f6/go.mod h1:zOssjAlNusOxvtaqT+EMA+Iyi8rrtKr4/XfzN1Fgoeg= | github.com/cnotch/queue v0.0.0-20201224060551-4191569ce8f6/go.mod h1:zOssjAlNusOxvtaqT+EMA+Iyi8rrtKr4/XfzN1Fgoeg= | ||||||
| github.com/cnotch/scheduler v0.0.0-20200522024700-1d2da93eefc5/go.mod h1:F4GE3SZkJZ8an1Y0ZCqvSM3jeozNuKzoC67erG1PhIo= | github.com/cnotch/scheduler v0.0.0-20200522024700-1d2da93eefc5/go.mod h1:F4GE3SZkJZ8an1Y0ZCqvSM3jeozNuKzoC67erG1PhIo= | ||||||
| github.com/cnotch/xlog v0.0.0-20201208005456-cfda439cd3a0/go.mod h1:RW9oHsR79ffl3sR3yMGgxYupMn2btzdtJUwoxFPUE5E= | github.com/cnotch/xlog v0.0.0-20201208005456-cfda439cd3a0/go.mod h1:RW9oHsR79ffl3sR3yMGgxYupMn2btzdtJUwoxFPUE5E= | ||||||
|  | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= | ||||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| @@ -23,12 +36,18 @@ github.com/edgeware/mp4ff v0.28.0/go.mod h1:GNUeA6tEFksH2CrjJF2FSGdJolba8yPGmo16 | |||||||
| github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug= | github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug= | ||||||
| github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | ||||||
| github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= | ||||||
|  | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= | ||||||
|  | github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= | ||||||
| github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= | ||||||
| github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= | ||||||
|  | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= | ||||||
|  | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= | ||||||
|  | github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= | ||||||
| github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= | ||||||
| github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= | ||||||
| github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= | github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= | ||||||
| github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= | github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= | ||||||
|  | github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= | ||||||
| github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= | github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= | ||||||
| github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= | ||||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
| @@ -50,25 +69,33 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | |||||||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||||
| github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= | ||||||
| github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||||
|  | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||||
| github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | ||||||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||||
| github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||||
| github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | ||||||
| github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= | ||||||
|  | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||||
| github.com/kelindar/process v0.0.0-20170730150328-69a29e249ec3/go.mod h1:+lTCLnZFXOkqwD8sLPl6u4erAc0cP8wFegQHfipz7KE= | github.com/kelindar/process v0.0.0-20170730150328-69a29e249ec3/go.mod h1:+lTCLnZFXOkqwD8sLPl6u4erAc0cP8wFegQHfipz7KE= | ||||||
| github.com/kelindar/rate v1.0.0/go.mod h1:AjT4G+hTItNwt30lucEGZIz8y7Uk5zPho6vurIZ+1Es= | github.com/kelindar/rate v1.0.0/go.mod h1:AjT4G+hTItNwt30lucEGZIz8y7Uk5zPho6vurIZ+1Es= | ||||||
| github.com/kelindar/tcp v1.0.0/go.mod h1:JB5hj1cshLU60XrLij2BBxW3JQ4hOye8vqbyvuKb52k= | github.com/kelindar/tcp v1.0.0/go.mod h1:JB5hj1cshLU60XrLij2BBxW3JQ4hOye8vqbyvuKb52k= | ||||||
|  | github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= | ||||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||||
|  | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= | ||||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||||
|  | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= | ||||||
| github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= | github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= | ||||||
| github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= | ||||||
| github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= | ||||||
| github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY= | github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY= | ||||||
| github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= | github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= | ||||||
|  | github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= | ||||||
| github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc= | github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc= | ||||||
| github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY= | github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY= | ||||||
|  | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= | ||||||
|  | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | ||||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= | ||||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= | ||||||
| github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= | ||||||
| @@ -85,34 +112,37 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J | |||||||
| github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= | ||||||
| github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= | github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= | ||||||
| github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= | github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= | ||||||
| github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ= | github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw= | ||||||
| github.com/pion/dtls/v2 v2.1.5/go.mod h1:BqCE7xPZbPSubGasRoDFJeTsyJtdD1FanJYL0JGheqY= | github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0= | ||||||
| github.com/pion/ice/v2 v2.2.12/go.mod h1:z2KXVFyRkmjetRlaVRgjO9U3ShKwzhlUylvxKfHfd5A= | github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= | ||||||
| github.com/pion/interceptor v0.1.11/go.mod h1:tbtKjZY14awXd7Bq0mmWvgtHB5MDaRN7HV3OZ/uy7s8= | github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0= | ||||||
|  | github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY= | ||||||
|  | github.com/pion/ice/v2 v2.3.1/go.mod h1:aq2kc6MtYNcn4XmMhobAv6hTNJiHzvD0yXRz80+bnP8= | ||||||
|  | github.com/pion/interceptor v0.1.12/go.mod h1:bDtgAD9dRkBZpWHGKaoKb42FhDHTG2rX8Ii9LRALLVA= | ||||||
| github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= | github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= | ||||||
| github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g= | github.com/pion/mdns v0.0.7/go.mod h1:4iP2UbeFhLI/vWju/bw6ZfwjJzk0z8DNValjGxR/dD8= | ||||||
| github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= | github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= | ||||||
| github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= | github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= | ||||||
| github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= |  | ||||||
| github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= | github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= | ||||||
| github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= | github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= | ||||||
| github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA= |  | ||||||
| github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= | github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= | ||||||
| github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= | github.com/pion/rtp v1.8.0 h1:SYD7040IR+NqrGBOc2GDU5iDjAR+0m5rnX/EWCUMNhw= | ||||||
| github.com/pion/sctp v1.8.3/go.mod h1:OHbDjdk7kg+L+7TJim9q/qGVefdEJohuA2SZyihccgI= | github.com/pion/rtp v1.8.0/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= | ||||||
|  | github.com/pion/sctp v1.8.5/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0= | ||||||
|  | github.com/pion/sctp v1.8.6/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0= | ||||||
| github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= | github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= | ||||||
| github.com/pion/srtp/v2 v2.0.10/go.mod h1:XEeSWaK9PfuMs7zxXyiN252AHPbH12NX5q/CFDWtUuA= | github.com/pion/srtp/v2 v2.0.12/go.mod h1:C3Ep44hlOo2qEYaq4ddsmK5dL63eLehXFbHaZ9F5V9Y= | ||||||
| github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= | github.com/pion/stun v0.4.0/go.mod h1:QPsh1/SbXASntw3zkkrIk3ZJVKz4saBY2G7S10P3wCw= | ||||||
| github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= | github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI= | ||||||
| github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= | github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc= | ||||||
| github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g= | github.com/pion/transport/v2 v2.0.2/go.mod h1:vrz6bUbFr/cjdwbnxq8OdDDzHf7JJfGsIRkxfpZoTA0= | ||||||
| github.com/pion/transport v0.13.1/go.mod h1:EBxbqzyv+ZrmDb82XswEE0BjfQFtuw1Nu6sjnjWCsGg= | github.com/pion/turn/v2 v2.1.0/go.mod h1:yrT5XbXSGX1VFSF31A3c1kCNB5bBZgk/uu5LET162qs= | ||||||
| github.com/pion/turn/v2 v2.0.8/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw= | github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8= | ||||||
| github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= | github.com/pion/webrtc/v3 v3.1.56 h1:ScaiqKQN3liQwT+kJwOBaYP6TwSfixzdUnZmzHAo0a0= | ||||||
| github.com/pion/webrtc/v3 v3.1.49 h1:rbsNGxK9jMYts+xE6zYAJMUQHnGwmk/JYze8yttW+to= | github.com/pion/webrtc/v3 v3.1.56/go.mod h1:7VhbA6ihqJlz6R/INHjyh1b8HpiV9Ct4UQvE1OB/xoM= | ||||||
| github.com/pion/webrtc/v3 v3.1.49/go.mod h1:kHf/o47QW4No1rgpsFux/h7lUhtUnwFnSFDZOXeLapw= |  | ||||||
| github.com/pixelbender/go-sdp v1.1.0/go.mod h1:6IBlz9+BrUHoFTea7gcp4S54khtOhjCW/nVDLhmZBAs= | github.com/pixelbender/go-sdp v1.1.0/go.mod h1:6IBlz9+BrUHoFTea7gcp4S54khtOhjCW/nVDLhmZBAs= | ||||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||||
|  | github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= | ||||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||||
| github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= | ||||||
| @@ -131,28 +161,30 @@ github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8u | |||||||
| github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA= | github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA= | ||||||
| github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo= | github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo= | ||||||
| github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= | github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= | ||||||
| github.com/shirou/gopsutil/v3 v3.22.10 h1:4KMHdfBRYXGF9skjDWiL4RA2N+E8dRdodU/bOZpPoVg= | github.com/shirou/gopsutil/v3 v3.22.11 h1:kxsPKS+Eeo+VnEQ2XCaGJepeP6KY53QoRTETx3+1ndM= | ||||||
| github.com/shirou/gopsutil/v3 v3.22.10/go.mod h1:QNza6r4YQoydyCfo6rH0blGfKahgibh4dQmV5xdFkQk= | github.com/shirou/gopsutil/v3 v3.22.11/go.mod h1:xl0EeL4vXJ+hQMAGN8B9VFpxukEMA0XdevQOe5MZ1oY= | ||||||
| github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A= | github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A= | ||||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||||
|  | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||||
| github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||||
| github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||||||
| github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= | github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I= | ||||||
| github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= | github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= | ||||||
| github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= | github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= | ||||||
| github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= |  | ||||||
| github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= | github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= | ||||||
| github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= | github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= | ||||||
| github.com/yapingcat/gomedia v0.0.0-20230426092936-387031404274 h1:cj4I+bvWX9I+Hg6tnZ7DAiOVxzhyLhdvYVKp+WpM/2c= | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= | ||||||
| github.com/yapingcat/gomedia v0.0.0-20230426092936-387031404274/go.mod h1:WSZ59bidJOO40JSJmLqlkBJrjZCtjbKKkygEMfzY/kc= | github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= | ||||||
|  | github.com/yapingcat/gomedia v0.0.0-20230727105416-c491e66c9d2a h1:x60q0A7QmoUTzixNz7zVTdEA9JC0oYqm8S51PdbTWgs= | ||||||
|  | github.com/yapingcat/gomedia v0.0.0-20230727105416-c491e66c9d2a/go.mod h1:WSZ59bidJOO40JSJmLqlkBJrjZCtjbKKkygEMfzY/kc= | ||||||
| github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||||
| github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | ||||||
| github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | ||||||
| @@ -164,17 +196,18 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 | |||||||
| go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= | go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= | ||||||
| go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= | go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= | ||||||
| go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= | go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= | ||||||
| go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= | go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= | ||||||
| go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= | ||||||
|  | golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= | ||||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
| golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= | ||||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||||
| golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= | ||||||
| golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= | ||||||
| golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= | golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= | ||||||
| golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= | golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= | ||||||
| golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= | ||||||
| golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= | ||||||
| golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||||
| @@ -187,19 +220,16 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn | |||||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
| golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||||
| golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||||
| golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= |  | ||||||
| golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= |  | ||||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||||
| golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | ||||||
| golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= | ||||||
| golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= |  | ||||||
| golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= |  | ||||||
| golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= |  | ||||||
| golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= |  | ||||||
| golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||||
| golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= | golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= | ||||||
| golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= | ||||||
| golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||||||
|  | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||||||
|  | golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= | ||||||
|  | golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= | ||||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| @@ -210,6 +240,7 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||||||
| golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| @@ -225,26 +256,28 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w | |||||||
| golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
|  | golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= | ||||||
|  | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= | ||||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||||
| golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||||
|  | golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= | ||||||
|  | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | ||||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||||
| golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||||
| golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||||
| golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||||
|  | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||||
|  | golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= | ||||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||||
| @@ -264,24 +297,28 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi | |||||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||||
| google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||||||
| google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||||||
| google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= | ||||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
|  | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= | ||||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
| gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||||||
| gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= | ||||||
|  | gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= | ||||||
|  | gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= | ||||||
| gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | ||||||
| gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
|  | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
| gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
| m7s.live/engine/v4 v4.12.6 h1:JWUwxOVHzL9wh8JbWUtZaDsRBXzyAzPI+4UEMGftsU0= | m7s.live/engine/v4 v4.13.8 h1:pDl8YWxip5aTidw2Q4NuU+8A6irBraLRfoeBi42S6iQ= | ||||||
| m7s.live/engine/v4 v4.12.6/go.mod h1:LoALBfV5rmsz5TJQr6cmLxM33mfUE5BKBq/sMtXOVlc= | m7s.live/engine/v4 v4.13.8/go.mod h1:k/6iFSuJxmhJL8VO45NAga8BbgZHLLfRXOwCcCzk2s8= | ||||||
| m7s.live/plugin/hls/v4 v4.0.0-20220619163635-447976e65ab9 h1:EcB8awppfwza+s4ECjUr3xLTtl9BgJcZ12EgfE/L2YA= | m7s.live/plugin/hls/v4 v4.3.1 h1:FXcQShWnNTE0Guxg6YMzb5pbvMpn6MKiwcZ05N6nzhs= | ||||||
| m7s.live/plugin/hls/v4 v4.0.0-20220619163635-447976e65ab9/go.mod h1:Qn4dDz5xlyBJwO+eZ3w8CUQ8Hl6KN1nmv0a3IsOjJvw= | m7s.live/plugin/hls/v4 v4.3.1/go.mod h1:HSV/yO9NMvmNkompn+faM/0en0t3q0axp4zEA2mdyWw= | ||||||
|   | |||||||
							
								
								
									
										55
									
								
								hls.go
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								hls.go
									
									
									
									
									
								
							| @@ -22,27 +22,31 @@ type HLSRecorder struct { | |||||||
| 	MemoryTs | 	MemoryTs | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewHLSRecorder() (r *HLSRecorder) { | ||||||
|  | 	r = &HLSRecorder{} | ||||||
|  | 	r.Record = RecordPluginConfig.Hls | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
| func (h *HLSRecorder) Start(streamPath string) error { | func (h *HLSRecorder) Start(streamPath string) error { | ||||||
| 	h.Record = &RecordPluginConfig.Hls |  | ||||||
| 	h.ID = streamPath + "/hls" | 	h.ID = streamPath + "/hls" | ||||||
| 	if _, ok := RecordPluginConfig.recordings.Load(h.ID); ok { | 	return h.start(h, streamPath, SUBTYPE_RAW) | ||||||
| 		return ErrRecordExist |  | ||||||
| 	} |  | ||||||
| 	h.BytesPool = make(util.BytesPool, 17) |  | ||||||
| 	return plugin.Subscribe(streamPath, h) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (h *HLSRecorder) OnEvent(event any) { | func (h *HLSRecorder) OnEvent(event any) { | ||||||
| 	var err error | 	var err error | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			h.Error("HLSRecorder Stop", zap.Error(err)) | 			h.Stop(zap.Error(err)) | ||||||
| 			h.Stop() |  | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| 	h.Recorder.OnEvent(event) |  | ||||||
| 	switch v := event.(type) { | 	switch v := event.(type) { | ||||||
| 	case *HLSRecorder: | 	case *HLSRecorder: | ||||||
|  | 		h.BytesPool = make(util.BytesPool, 17) | ||||||
|  | 		if h.Writer, err = h.createFile(); err != nil { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		h.SetIO(h.Writer) | ||||||
| 		h.playlist = hls.Playlist{ | 		h.playlist = hls.Playlist{ | ||||||
| 			Writer:         h.Writer, | 			Writer:         h.Writer, | ||||||
| 			Version:        3, | 			Version:        3, | ||||||
| @@ -52,12 +56,11 @@ func (h *HLSRecorder) OnEvent(event any) { | |||||||
| 		if err = h.playlist.Init(); err != nil { | 		if err = h.playlist.Init(); err != nil { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		if err = h.createHlsTsSegmentFile(); err != nil { | 		if h.File, err = h.CreateFile(); err != nil { | ||||||
| 			h.Stop() |  | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		go h.start() |  | ||||||
| 	case AudioFrame: | 	case AudioFrame: | ||||||
|  | 		h.Recorder.OnEvent(event) | ||||||
| 		pes := &mpegts.MpegtsPESFrame{ | 		pes := &mpegts.MpegtsPESFrame{ | ||||||
| 			Pid:                       mpegts.PID_AUDIO, | 			Pid:                       mpegts.PID_AUDIO, | ||||||
| 			IsKeyFrame:                false, | 			IsKeyFrame:                false, | ||||||
| @@ -65,18 +68,12 @@ func (h *HLSRecorder) OnEvent(event any) { | |||||||
| 			ProgramClockReferenceBase: uint64(v.DTS), | 			ProgramClockReferenceBase: uint64(v.DTS), | ||||||
| 		} | 		} | ||||||
| 		h.WriteAudioFrame(v, pes) | 		h.WriteAudioFrame(v, pes) | ||||||
| 		h.BLL.WriteTo(h) | 		h.BLL.WriteTo(h.File) | ||||||
| 		h.Recycle() | 		h.Recycle() | ||||||
| 		h.Clear() | 		h.Clear() | ||||||
| 		h.audio_cc = pes.ContinuityCounter | 		h.audio_cc = pes.ContinuityCounter | ||||||
| 	case VideoFrame: | 	case VideoFrame: | ||||||
| 		if h.Fragment != 0 && h.newFile { | 		h.Recorder.OnEvent(event) | ||||||
| 			h.newFile = false |  | ||||||
| 			h.Close() |  | ||||||
| 			if err = h.createHlsTsSegmentFile(); err != nil { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		pes := &mpegts.MpegtsPESFrame{ | 		pes := &mpegts.MpegtsPESFrame{ | ||||||
| 			Pid:                       mpegts.PID_VIDEO, | 			Pid:                       mpegts.PID_VIDEO, | ||||||
| 			IsKeyFrame:                v.IFrame, | 			IsKeyFrame:                v.IFrame, | ||||||
| @@ -86,21 +83,25 @@ func (h *HLSRecorder) OnEvent(event any) { | |||||||
| 		if err = h.WriteVideoFrame(v, pes); err != nil { | 		if err = h.WriteVideoFrame(v, pes); err != nil { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		h.BLL.WriteTo(h) | 		h.BLL.WriteTo(h.File) | ||||||
| 		h.Recycle() | 		h.Recycle() | ||||||
| 		h.Clear() | 		h.Clear() | ||||||
| 		h.video_cc = pes.ContinuityCounter | 		h.video_cc = pes.ContinuityCounter | ||||||
|  | 	default: | ||||||
|  | 		h.Recorder.OnEvent(v) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // 创建一个新的ts文件 | // 创建一个新的ts文件 | ||||||
| func (h *HLSRecorder) createHlsTsSegmentFile() (err error) { | func (h *HLSRecorder) CreateFile() (fw FileWr, err error) { | ||||||
| 	tsFilename := strconv.FormatInt(time.Now().Unix(), 10) + ".ts" | 	tsFilename := strconv.FormatInt(time.Now().Unix(), 10) + ".ts" | ||||||
| 	fw, err := h.CreateFileFn(filepath.Join(h.Stream.Path, tsFilename), false) | 	filePath := filepath.Join(h.Stream.Path, tsFilename) | ||||||
|  | 	fw, err = h.CreateFileFn(filePath, false) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		h.Error("create file", zap.String("path", filePath), zap.Error(err)) | ||||||
|  | 		return | ||||||
| 	} | 	} | ||||||
| 	h.SetIO(fw) | 	h.Info("create file", zap.String("path", filePath)) | ||||||
| 	inf := hls.PlaylistInf{ | 	inf := hls.PlaylistInf{ | ||||||
| 		Duration: h.Fragment.Seconds(), | 		Duration: h.Fragment.Seconds(), | ||||||
| 		Title:    tsFilename, | 		Title:    tsFilename, | ||||||
| @@ -109,7 +110,7 @@ func (h *HLSRecorder) createHlsTsSegmentFile() (err error) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if err = mpegts.WriteDefaultPATPacket(fw); err != nil { | 	if err = mpegts.WriteDefaultPATPacket(fw); err != nil { | ||||||
| 		return err | 		return | ||||||
| 	} | 	} | ||||||
| 	var vcodec codec.VideoCodecID = 0 | 	var vcodec codec.VideoCodecID = 0 | ||||||
| 	var acodec codec.AudioCodecID = 0 | 	var acodec codec.AudioCodecID = 0 | ||||||
| @@ -120,5 +121,5 @@ func (h *HLSRecorder) createHlsTsSegmentFile() (err error) { | |||||||
| 		acodec = h.Audio.CodecID | 		acodec = h.Audio.CodecID | ||||||
| 	} | 	} | ||||||
| 	mpegts.WritePMTPacket(fw, vcodec, acodec) | 	mpegts.WritePMTPacket(fw, vcodec, acodec) | ||||||
| 	return err | 	return | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								main.go
									
									
									
									
									
								
							| @@ -17,6 +17,7 @@ type RecordConfig struct { | |||||||
| 	config.Subscribe | 	config.Subscribe | ||||||
| 	Flv        Record | 	Flv        Record | ||||||
| 	Mp4        Record | 	Mp4        Record | ||||||
|  | 	Fmp4       Record | ||||||
| 	Hls        Record | 	Hls        Record | ||||||
| 	Raw        Record | 	Raw        Record | ||||||
| 	RawAudio   Record | 	RawAudio   Record | ||||||
| @@ -33,6 +34,10 @@ var RecordPluginConfig = &RecordConfig{ | |||||||
| 		Ext:           ".flv", | 		Ext:           ".flv", | ||||||
| 		GetDurationFn: getFLVDuration, | 		GetDurationFn: getFLVDuration, | ||||||
| 	}, | 	}, | ||||||
|  | 	Fmp4: Record{ | ||||||
|  | 		Path: "record/fmp4", | ||||||
|  | 		Ext:  ".mp4", | ||||||
|  | 	}, | ||||||
| 	Mp4: Record{ | 	Mp4: Record{ | ||||||
| 		Path: "record/mp4", | 		Path: "record/mp4", | ||||||
| 		Ext:  ".mp4", | 		Ext:  ".mp4", | ||||||
| @@ -58,43 +63,29 @@ func (conf *RecordConfig) OnEvent(event any) { | |||||||
| 	case FirstConfig, config.Config: | 	case FirstConfig, config.Config: | ||||||
| 		conf.Flv.Init() | 		conf.Flv.Init() | ||||||
| 		conf.Mp4.Init() | 		conf.Mp4.Init() | ||||||
|  | 		conf.Fmp4.Init() | ||||||
| 		conf.Hls.Init() | 		conf.Hls.Init() | ||||||
| 		conf.Raw.Init() | 		conf.Raw.Init() | ||||||
| 		conf.RawAudio.Init() | 		conf.RawAudio.Init() | ||||||
| 	case SEclose: |  | ||||||
| 		streamPath := v.Target.Path |  | ||||||
| 		delete(conf.Flv.recording, streamPath) |  | ||||||
| 		delete(conf.Mp4.recording, streamPath) |  | ||||||
| 		delete(conf.Hls.recording, streamPath) |  | ||||||
| 		delete(conf.Raw.recording, streamPath) |  | ||||||
| 		delete(conf.RawAudio.recording, streamPath) |  | ||||||
| 	case SEpublish: | 	case SEpublish: | ||||||
| 		streamPath := v.Target.Path | 		streamPath := v.Target.Path | ||||||
| 		if conf.Flv.NeedRecord(streamPath) { | 		if conf.Flv.NeedRecord(streamPath) { | ||||||
| 			var flv FLVRecorder | 			go NewFLVRecorder().Start(streamPath) | ||||||
| 			conf.Flv.recording[streamPath] = &flv |  | ||||||
| 			go flv.Start(streamPath) |  | ||||||
| 		} | 		} | ||||||
| 		if conf.Mp4.NeedRecord(streamPath) { | 		if conf.Mp4.NeedRecord(streamPath) { | ||||||
| 			recoder := NewMP4Recorder() | 			go NewMP4Recorder().Start(streamPath) | ||||||
| 			conf.Mp4.recording[streamPath] = recoder | 		} | ||||||
| 			go recoder.Start(streamPath) | 		if conf.Fmp4.NeedRecord(streamPath) { | ||||||
|  | 			go NewFMP4Recorder().Start(streamPath) | ||||||
| 		} | 		} | ||||||
| 		if conf.Hls.NeedRecord(streamPath) { | 		if conf.Hls.NeedRecord(streamPath) { | ||||||
| 			var hls HLSRecorder | 			go NewHLSRecorder().Start(streamPath) | ||||||
| 			conf.Hls.recording[streamPath] = &hls |  | ||||||
| 			go hls.Start(streamPath) |  | ||||||
| 		} | 		} | ||||||
| 		if conf.Raw.NeedRecord(streamPath) { | 		if conf.Raw.NeedRecord(streamPath) { | ||||||
| 			var raw RawRecorder | 			go NewRawRecorder().Start(streamPath) | ||||||
| 			conf.Raw.recording[streamPath] = &raw |  | ||||||
| 			go raw.Start(streamPath) |  | ||||||
| 		} | 		} | ||||||
| 		if conf.RawAudio.NeedRecord(streamPath) { | 		if conf.RawAudio.NeedRecord(streamPath) { | ||||||
| 			var raw RawRecorder | 			go NewRawAudioRecorder().Start(streamPath) | ||||||
| 			raw.IsAudio = true |  | ||||||
| 			conf.RawAudio.recording[streamPath] = &raw |  | ||||||
| 			go raw.Start(streamPath) |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -104,6 +95,8 @@ func (conf *RecordConfig) getRecorderConfigByType(t string) (recorder *Record) { | |||||||
| 		recorder = &conf.Flv | 		recorder = &conf.Flv | ||||||
| 	case "mp4": | 	case "mp4": | ||||||
| 		recorder = &conf.Mp4 | 		recorder = &conf.Mp4 | ||||||
|  | 	case "fmp4": | ||||||
|  | 		recorder = &conf.Fmp4 | ||||||
| 	case "hls": | 	case "hls": | ||||||
| 		recorder = &conf.Hls | 		recorder = &conf.Hls | ||||||
| 	case "raw": | 	case "raw": | ||||||
|   | |||||||
							
								
								
									
										194
									
								
								mp4.go
									
									
									
									
									
								
							
							
						
						
									
										194
									
								
								mp4.go
									
									
									
									
									
								
							| @@ -1,178 +1,82 @@ | |||||||
| package record | package record | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"path/filepath" | 	"github.com/yapingcat/gomedia/go-mp4" | ||||||
| 	"strconv" | 	"go.uber.org/zap" | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/edgeware/mp4ff/aac" |  | ||||||
| 	"github.com/edgeware/mp4ff/mp4" |  | ||||||
| 	. "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/util" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type mediaContext struct { |  | ||||||
| 	trackId  uint32 |  | ||||||
| 	fragment *mp4.Fragment |  | ||||||
| 	ts       uint32 // 每个小片段起始时间戳 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *mediaContext) push(recoder *MP4Recorder, dt uint32, dur uint32, data []byte, flags uint32) { |  | ||||||
| 	if m.fragment != nil && dt-m.ts > 1000 { |  | ||||||
| 		m.fragment.Encode(recoder) |  | ||||||
| 		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 MP4Recorder struct { | type MP4Recorder struct { | ||||||
| 	Recorder | 	Recorder | ||||||
| 	*mp4.InitSegment `json:"-" yaml:"-"` | 	*mp4.Movmuxer `json:"-" yaml:"-"` | ||||||
| 	video            mediaContext | 	videoId       uint32 | ||||||
| 	audio            mediaContext | 	audioId       uint32 | ||||||
| 	seqNumber        uint32 |  | ||||||
| 	ftyp             *mp4.FtypBox |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewMP4Recorder() *MP4Recorder { | func NewMP4Recorder() *MP4Recorder { | ||||||
| 	r := &MP4Recorder{ | 	r := &MP4Recorder{} | ||||||
| 		InitSegment: mp4.CreateEmptyInit(), | 	r.Record = RecordPluginConfig.Mp4 | ||||||
| 	} |  | ||||||
| 	r.Moov.Mvhd.NextTrackID = 1 |  | ||||||
| 	return r | 	return r | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *MP4Recorder) Start(streamPath string) (err error) { | func (r *MP4Recorder) Start(streamPath string) (err error) { | ||||||
| 	r.Record = &RecordPluginConfig.Mp4 |  | ||||||
| 	r.ID = streamPath + "/mp4" | 	r.ID = streamPath + "/mp4" | ||||||
| 	if _, ok := RecordPluginConfig.recordings.Load(r.ID); ok { | 	return r.start(r, streamPath, SUBTYPE_RAW) | ||||||
| 		return ErrRecordExist |  | ||||||
| 	} |  | ||||||
| 	return plugin.Subscribe(streamPath, r) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *MP4Recorder) Close() error { | func (r *MP4Recorder) Close() (err error) { | ||||||
| 	if r.Writer != nil { | 	if r.File != nil { | ||||||
| 		if r.video.fragment != nil { | 		err = r.Movmuxer.WriteTrailer() | ||||||
| 			r.video.fragment.Encode(r.Writer) | 		if err != nil { | ||||||
| 			r.video.fragment = nil | 			r.Error("mp4 write trailer", zap.Error(err)) | ||||||
|  | 		} else { | ||||||
|  | 			// _, err = r.file.Write(r.cache.buf) | ||||||
|  | 			r.Info("mp4 write trailer", zap.Error(err)) | ||||||
| 		} | 		} | ||||||
| 		if r.audio.fragment != nil { | 		err = r.File.Close() | ||||||
| 			r.audio.fragment.Encode(r.Writer) |  | ||||||
| 			r.audio.fragment = nil |  | ||||||
| 		} |  | ||||||
| 		r.Closer.Close() |  | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return | ||||||
| } | } | ||||||
|  | func (r *MP4Recorder) setTracks() { | ||||||
| func (r *MP4Recorder) OnEvent(event any) { | 	if r.Audio != nil { | ||||||
| 	r.Recorder.OnEvent(event) | 		switch r.Audio.CodecID { | ||||||
| 	if 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) |  | ||||||
| 			r.InitSegment = mp4.CreateEmptyInit() |  | ||||||
| 			r.Moov.Mvhd.NextTrackID = 1 |  | ||||||
| 			if r.VideoReader != nil { |  | ||||||
| 				r.OnEvent(r.Video) |  | ||||||
| 			} |  | ||||||
| 			if r.AudioReader != nil { |  | ||||||
| 				r.OnEvent(r.Audio) |  | ||||||
| 			} |  | ||||||
| 			r.ftyp.Encode(r) |  | ||||||
| 			r.Moov.Encode(r) |  | ||||||
| 			r.seqNumber = 0 |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	switch v := event.(type) { |  | ||||||
| 	case *track.Video: |  | ||||||
| 		moov := r.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 v.CodecID { |  | ||||||
| 		case codec.CodecID_H264: |  | ||||||
| 			r.ftyp = mp4.NewFtyp("isom", 0x200, []string{ |  | ||||||
| 				"isom", "iso2", "avc1", "mp41", |  | ||||||
| 			}) |  | ||||||
| 			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.ParamaterSets[0:1], v.ParamaterSets[1:2], v.ParamaterSets[2:3], true) |  | ||||||
| 		} |  | ||||||
| 		r.AddTrack(v) |  | ||||||
| 	case *track.Audio: |  | ||||||
| 		moov := r.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 v.CodecID { |  | ||||||
| 		case codec.CodecID_AAC: | 		case codec.CodecID_AAC: | ||||||
| 			switch v.AudioObjectType { | 			r.audioId = r.AddAudioTrack(mp4.MP4_CODEC_AAC) | ||||||
| 			case 1: |  | ||||||
| 				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: | 		case codec.CodecID_PCMA: | ||||||
| 			stsd := newTrak.Mdia.Minf.Stbl.Stsd | 			r.audioId = r.AddAudioTrack(mp4.MP4_CODEC_G711A) | ||||||
| 			pcma := mp4.CreateAudioSampleEntryBox("pcma", |  | ||||||
| 				uint16(v.Channels), |  | ||||||
| 				uint16(v.SampleSize), uint16(v.SampleRate), nil) |  | ||||||
| 			stsd.AddChild(pcma) |  | ||||||
| 		case codec.CodecID_PCMU: | 		case codec.CodecID_PCMU: | ||||||
| 			stsd := newTrak.Mdia.Minf.Stbl.Stsd | 			r.audioId = r.AddAudioTrack(mp4.MP4_CODEC_G711U) | ||||||
| 			pcmu := mp4.CreateAudioSampleEntryBox("pcmu", |  | ||||||
| 				uint16(v.Channels), |  | ||||||
| 				uint16(v.SampleSize), uint16(v.SampleRate), nil) |  | ||||||
| 			stsd.AddChild(pcmu) |  | ||||||
| 		} | 		} | ||||||
| 		r.AddTrack(v) | 	} | ||||||
| 	case ISubscriber: | 	if r.Video != nil { | ||||||
| 		if r.ftyp != nil && r.Writer != nil { | 		switch r.Video.CodecID { | ||||||
| 			r.ftyp.Encode(r) | 		case codec.CodecID_H264: | ||||||
| 			r.Moov.Encode(r) | 			r.videoId = r.AddVideoTrack(mp4.MP4_CODEC_H264) | ||||||
| 			go r.start() | 		case codec.CodecID_H265: | ||||||
|  | 			r.videoId = r.AddVideoTrack(mp4.MP4_CODEC_H265) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | func (r *MP4Recorder) OnEvent(event any) { | ||||||
|  | 	var err error | ||||||
|  | 	r.Recorder.OnEvent(event) | ||||||
|  | 	switch v := event.(type) { | ||||||
|  | 	case FileWr: | ||||||
|  | 		r.Movmuxer, err = mp4.CreateMp4Muxer(v) | ||||||
|  | 		if err != nil { | ||||||
|  | 			r.Error("mp4 create muxer", zap.Error(err)) | ||||||
|  | 		} else { | ||||||
|  | 			r.setTracks() | ||||||
| 		} | 		} | ||||||
| 	case AudioFrame: | 	case AudioFrame: | ||||||
| 		if r.audio.trackId != 0 { | 		if r.audioId != 0 { | ||||||
| 			r.audio.push(r, v.AbsTime, v.DeltaTime, v.AUList.ToBytes(), mp4.SyncSampleFlags) | 			r.Write(r.audioId, util.ConcatBuffers(v.GetADTS()), uint64(v.AbsTime+(v.PTS-v.DTS)/90), uint64(v.AbsTime)) | ||||||
| 		} | 		} | ||||||
| 	case VideoFrame: | 	case VideoFrame: | ||||||
| 		if r.video.trackId != 0 { | 		if r.videoId != 0 { | ||||||
| 			flag := mp4.NonSyncSampleFlags | 			r.Write(r.videoId, util.ConcatBuffers(v.GetAnnexB()), uint64(v.AbsTime+(v.PTS-v.DTS)/90), uint64(v.AbsTime)) | ||||||
| 			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) |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										63
									
								
								raw.go
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								raw.go
									
									
									
									
									
								
							| @@ -1,11 +1,6 @@ | |||||||
| package record | package record | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"path/filepath" |  | ||||||
| 	"strconv" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"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/track" | 	"m7s.live/engine/v4/track" | ||||||
| @@ -16,38 +11,32 @@ type RawRecorder struct { | |||||||
| 	IsAudio bool | 	IsAudio bool | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewRawRecorder() (r *RawRecorder) { | ||||||
|  | 	r = &RawRecorder{} | ||||||
|  | 	r.Record = RecordPluginConfig.Raw | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewRawAudioRecorder() (r *RawRecorder) { | ||||||
|  | 	r = &RawRecorder{IsAudio: true} | ||||||
|  | 	r.Record = RecordPluginConfig.RawAudio | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
| func (r *RawRecorder) Start(streamPath string) error { | func (r *RawRecorder) Start(streamPath string) error { | ||||||
| 	if r.IsAudio { |  | ||||||
| 		r.Record = &RecordPluginConfig.RawAudio |  | ||||||
| 	} else { |  | ||||||
| 		r.Record = &RecordPluginConfig.Raw |  | ||||||
| 	} |  | ||||||
| 	r.ID = streamPath + "/raw" | 	r.ID = streamPath + "/raw" | ||||||
| 	if r.IsAudio { | 	if r.IsAudio { | ||||||
| 		r.ID += "_audio" | 		r.ID += "_audio" | ||||||
| 	} | 	} | ||||||
| 	if _, ok := RecordPluginConfig.recordings.Load(r.ID); ok { | 	return r.start(r, streamPath, SUBTYPE_RAW) | ||||||
| 		return ErrRecordExist |  | ||||||
| 	} |  | ||||||
| 	return plugin.Subscribe(streamPath, r) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *RawRecorder) OnEvent(event any) { | func (r *RawRecorder) OnEvent(event any) { | ||||||
| 	switch v := event.(type) { | 	switch v := event.(type) { | ||||||
|  | 	case FileWr: | ||||||
|  | 		r.SetIO(v) | ||||||
| 	case *RawRecorder: | 	case *RawRecorder: | ||||||
| 		filename := strconv.FormatInt(time.Now().Unix(), 10) + r.Ext | 		r.Recorder.OnEvent(event) | ||||||
| 		if r.Fragment == 0 { |  | ||||||
| 			filename = r.Stream.Path + r.Ext |  | ||||||
| 		} else { |  | ||||||
| 			filename = filepath.Join(r.Stream.Path, filename) |  | ||||||
| 		} |  | ||||||
| 		if file, err := r.CreateFileFn(filename, r.append); err == nil { |  | ||||||
| 			r.SetIO(file) |  | ||||||
| 		} else { |  | ||||||
| 			r.Error("create file failed", zap.Error(err)) |  | ||||||
| 			r.Stop() |  | ||||||
| 		} |  | ||||||
| 		go r.start() |  | ||||||
| 	case *track.Video: | 	case *track.Video: | ||||||
| 		if r.IsAudio { | 		if r.IsAudio { | ||||||
| 			break | 			break | ||||||
| @@ -76,26 +65,10 @@ func (r *RawRecorder) OnEvent(event any) { | |||||||
| 		} | 		} | ||||||
| 		r.AddTrack(v) | 		r.AddTrack(v) | ||||||
| 	case AudioFrame: | 	case AudioFrame: | ||||||
| 		if r.Fragment > 0 { | 		r.Recorder.OnEvent(event) | ||||||
| 			if r.cut(v.AbsTime); 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) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		v.WriteRawTo(r) | 		v.WriteRawTo(r) | ||||||
| 	case VideoFrame: | 	case VideoFrame: | ||||||
| 		if r.Fragment > 0 && v.IFrame { | 		r.Recorder.OnEvent(event) | ||||||
| 			if r.cut(v.AbsTime); 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) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		v.WriteAnnexBTo(r) | 		v.WriteAnnexBTo(r) | ||||||
| 	default: | 	default: | ||||||
| 		r.IO.OnEvent(v) | 		r.IO.OnEvent(v) | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								restful.go
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								restful.go
									
									
									
									
									
								
							| @@ -2,6 +2,7 @@ package record | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| @@ -16,7 +17,7 @@ func (conf *RecordConfig) API_list(w http.ResponseWriter, r *http.Request) { | |||||||
| 	var err error | 	var err error | ||||||
| 	recorder := conf.getRecorderConfigByType(t) | 	recorder := conf.getRecorderConfigByType(t) | ||||||
| 	if recorder == nil { | 	if recorder == nil { | ||||||
| 		for _, t = range []string{"flv", "mp4", "hls", "raw", "raw_audio"} { | 		for _, t = range []string{"flv", "mp4", "fmp4", "hls", "raw", "raw_audio"} { | ||||||
| 			recorder = conf.getRecorderConfigByType(t) | 			recorder = conf.getRecorderConfigByType(t) | ||||||
| 			var fs []*VideoFileInfo | 			var fs []*VideoFileInfo | ||||||
| 			if fs, err = recorder.Tree(recorder.Path, 0); err == nil { | 			if fs, err = recorder.Tree(recorder.Path, 0); err == nil { | ||||||
| @@ -41,6 +42,8 @@ func (conf *RecordConfig) API_list(w http.ResponseWriter, r *http.Request) { | |||||||
| func (conf *RecordConfig) API_start(w http.ResponseWriter, r *http.Request) { | func (conf *RecordConfig) API_start(w http.ResponseWriter, r *http.Request) { | ||||||
| 	query := r.URL.Query() | 	query := r.URL.Query() | ||||||
| 	streamPath := query.Get("streamPath") | 	streamPath := query.Get("streamPath") | ||||||
|  | 	fileName := query.Get("fileName") | ||||||
|  | 	fragment := query.Get("fragment") | ||||||
| 	if streamPath == "" { | 	if streamPath == "" { | ||||||
| 		http.Error(w, "no streamPath", http.StatusBadRequest) | 		http.Error(w, "no streamPath", http.StatusBadRequest) | ||||||
| 		return | 		return | ||||||
| @@ -48,43 +51,40 @@ func (conf *RecordConfig) API_start(w http.ResponseWriter, r *http.Request) { | |||||||
| 	t := query.Get("type") | 	t := query.Get("type") | ||||||
| 	var id string | 	var id string | ||||||
| 	var err error | 	var err error | ||||||
|  | 	var irecorder IRecorder | ||||||
| 	switch t { | 	switch t { | ||||||
| 	case "": | 	case "": | ||||||
| 		t = "flv" | 		t = "flv" | ||||||
| 		fallthrough | 		fallthrough | ||||||
| 	case "flv": | 	case "flv": | ||||||
| 		var flvRecoder FLVRecorder | 		irecorder = NewFLVRecorder() | ||||||
| 		flvRecoder.append = query.Get("append") != "" |  | ||||||
| 		err = flvRecoder.Start(streamPath) |  | ||||||
| 		id = flvRecoder.ID |  | ||||||
| 	case "mp4": | 	case "mp4": | ||||||
| 		recorder := NewMP4Recorder() | 		irecorder = NewMP4Recorder() | ||||||
| 		err = recorder.Start(streamPath) | 	case "fmp4": | ||||||
| 		id = recorder.ID | 		irecorder = NewFMP4Recorder() | ||||||
| 	case "hls": | 	case "hls": | ||||||
| 		var recorder HLSRecorder | 		irecorder = NewHLSRecorder() | ||||||
| 		err = recorder.Start(streamPath) |  | ||||||
| 		id = recorder.ID |  | ||||||
| 	case "raw": | 	case "raw": | ||||||
| 		var recorder RawRecorder | 		irecorder = NewRawRecorder() | ||||||
| 		recorder.append = query.Get("append") != "" |  | ||||||
| 		err = recorder.Start(streamPath) |  | ||||||
| 		id = recorder.ID |  | ||||||
| 	case "raw_audio": | 	case "raw_audio": | ||||||
| 		var recorder RawRecorder | 		irecorder = NewRawAudioRecorder() | ||||||
| 		recorder.IsAudio = true |  | ||||||
| 		recorder.append = query.Get("append") != "" |  | ||||||
| 		err = recorder.Start(streamPath) |  | ||||||
| 		id = recorder.ID |  | ||||||
| 	default: | 	default: | ||||||
| 		http.Error(w, "type not supported", http.StatusBadRequest) | 		http.Error(w, "type not supported", http.StatusBadRequest) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	recorder := irecorder.GetRecorder() | ||||||
|  | 	if fragment != "" { | ||||||
|  | 		recorder.Fragment, err = time.ParseDuration(fragment) | ||||||
|  | 	} | ||||||
|  | 	recorder.FileName = fileName | ||||||
|  | 	recorder.append = query.Get("append") != "" | ||||||
|  | 	err = irecorder.Start(streamPath) | ||||||
|  | 	id = recorder.ID | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		http.Error(w, err.Error(), http.StatusInternalServerError) | 		http.Error(w, err.Error(), http.StatusInternalServerError) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	w.Write([]byte(id)) | 	fmt.Fprintf(w, id) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (conf *RecordConfig) API_list_recording(w http.ResponseWriter, r *http.Request) { | func (conf *RecordConfig) API_list_recording(w http.ResponseWriter, r *http.Request) { | ||||||
|   | |||||||
							
								
								
									
										104
									
								
								subscriber.go
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								subscriber.go
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| package record | package record | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"io" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
| @@ -9,41 +10,100 @@ import ( | |||||||
| 	. "m7s.live/engine/v4" | 	. "m7s.live/engine/v4" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Recorder struct { | type IRecorder interface { | ||||||
| 	Subscriber | 	ISubscriber | ||||||
| 	SkipTS  uint32 | 	GetRecorder() *Recorder | ||||||
| 	*Record `json:"-" yaml:"-"` | 	Start(streamPath string) error | ||||||
| 	newFile bool // 创建了新的文件 | 	io.Closer | ||||||
| 	append  bool // 是否追加模式 | 	CreateFile() (FileWr, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *Recorder) start() { | type Recorder struct { | ||||||
| 	RecordPluginConfig.recordings.Store(r.ID, r) | 	Subscriber | ||||||
| 	r.PlayRaw() | 	SkipTS   uint32 | ||||||
| 	RecordPluginConfig.recordings.Delete(r.ID) | 	Record   `json:"-" yaml:"-"` | ||||||
|  | 	File     FileWr `json:"-" yaml:"-"` | ||||||
|  | 	FileName string // 自定义文件名,分段录像无效 | ||||||
|  | 	append   bool   // 是否追加模式 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Recorder) GetRecorder() *Recorder { | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Recorder) CreateFile() (FileWr, error) { | ||||||
|  | 	return r.createFile() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Recorder) Close() error { | ||||||
|  | 	if r.File != nil { | ||||||
|  | 		return r.File.Close() | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Recorder) createFile() (f FileWr, err error) { | ||||||
|  | 	filePath := r.getFileName(r.Stream.Path) + r.Ext | ||||||
|  | 	f, err = r.CreateFileFn(filePath, r.append) | ||||||
|  | 	if err == nil { | ||||||
|  | 		r.Info("create file", zap.String("path", filePath)) | ||||||
|  | 	} else { | ||||||
|  | 		r.Error("create file", zap.String("path", filePath), zap.Error(err)) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Recorder) getFileName(streamPath string) (filename string) { | ||||||
|  | 	filename = streamPath | ||||||
|  | 	if r.Fragment == 0 { | ||||||
|  | 		if r.FileName != "" { | ||||||
|  | 			filename = filepath.Join(filename, r.FileName) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		filename = filepath.Join(filename, strconv.FormatInt(time.Now().Unix(), 10)) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Recorder) start(re IRecorder, streamPath string, subType byte) (err error) { | ||||||
|  | 	err = plugin.Subscribe(streamPath, re) | ||||||
|  | 	if err == nil { | ||||||
|  | 		if _, loaded := RecordPluginConfig.recordings.LoadOrStore(r.ID, re); loaded { | ||||||
|  | 			return ErrRecordExist | ||||||
|  | 		} | ||||||
|  | 		r.recording[streamPath] = re | ||||||
|  | 		r.Closer = re | ||||||
|  | 		go func() { | ||||||
|  | 			r.PlayBlock(subType) | ||||||
|  | 			RecordPluginConfig.recordings.Delete(r.ID) | ||||||
|  | 			delete(r.recording, streamPath) | ||||||
|  | 			re.Close() | ||||||
|  | 		}() | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *Recorder) cut(absTime uint32) { | func (r *Recorder) cut(absTime uint32) { | ||||||
| 	if ts := absTime - r.SkipTS; time.Duration(ts)*time.Millisecond >= r.Fragment { | 	if ts := absTime - r.SkipTS; time.Duration(ts)*time.Millisecond >= r.Fragment { | ||||||
| 		r.SkipTS = absTime | 		r.SkipTS = absTime | ||||||
| 		r.newFile = true | 		r.Close() | ||||||
|  | 		if file, err := r.Spesific.(IRecorder).CreateFile(); err == nil { | ||||||
|  | 			r.File = file | ||||||
|  | 			r.Spesific.OnEvent(file) | ||||||
|  | 		} else { | ||||||
|  | 			r.Stop(zap.Error(err)) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *Recorder) OnEvent(event any) { | func (r *Recorder) OnEvent(event any) { | ||||||
| 	switch v := event.(type) { | 	switch v := event.(type) { | ||||||
| 	case ISubscriber: | 	case IRecorder: | ||||||
| 		filename := strconv.FormatInt(time.Now().Unix(), 10) + r.Ext | 		if file, err := r.Spesific.(IRecorder).CreateFile(); err == nil { | ||||||
| 		if r.Fragment == 0 { | 			r.File = file | ||||||
| 			filename = r.Stream.Path + r.Ext | 			r.Spesific.OnEvent(file) | ||||||
| 		} else { | 		} else { | ||||||
| 			filename = filepath.Join(r.Stream.Path, filename) | 			r.Stop(zap.Error(err)) | ||||||
| 		} |  | ||||||
| 		if file, err := r.CreateFileFn(filename, r.append); err == nil { |  | ||||||
| 			r.SetIO(file) |  | ||||||
| 		} else { |  | ||||||
| 			r.Error("create file failed", zap.Error(err)) |  | ||||||
| 			r.Stop() |  | ||||||
| 		} | 		} | ||||||
| 	case AudioFrame: | 	case AudioFrame: | ||||||
| 		// 纯音频流的情况下需要切割文件 | 		// 纯音频流的情况下需要切割文件 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 langhuihui
					langhuihui