From 079d712e88feebd5baab4e78f9ce0359074149e0 Mon Sep 17 00:00:00 2001 From: langhuihui <178529795@qq.com> Date: Thu, 25 May 2023 14:17:50 +0800 Subject: [PATCH] feat: add raw_audio recoder --- main.go | 15 +++++++++++++ raw.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++------ restful.go | 25 +++++++++++++-------- 3 files changed, 88 insertions(+), 16 deletions(-) diff --git a/main.go b/main.go index 0f5552a..33eb63e 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ type RecordConfig struct { Mp4 Record Hls Record Raw Record + RawAudio Record recordings sync.Map } @@ -44,6 +45,10 @@ var RecordPluginConfig = &RecordConfig{ Path: "record/raw", Ext: ".", // 默认h264扩展名为.h264,h265扩展名为.h265 }, + RawAudio: Record{ + Path: "record/raw", + Ext: ".", // 默认aac扩展名为.aac,pcma扩展名为.pcma,pcmu扩展名为.pcmu + }, } var plugin = InstallPlugin(RecordPluginConfig) @@ -55,12 +60,14 @@ func (conf *RecordConfig) OnEvent(event any) { conf.Mp4.Init() conf.Hls.Init() conf.Raw.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: streamPath := v.Target.Path if conf.Flv.NeedRecord(streamPath) { @@ -83,6 +90,12 @@ func (conf *RecordConfig) OnEvent(event any) { conf.Raw.recording[streamPath] = &raw go raw.Start(streamPath) } + if conf.RawAudio.NeedRecord(streamPath) { + var raw RawRecorder + raw.IsAudio = true + conf.RawAudio.recording[streamPath] = &raw + go raw.Start(streamPath) + } } } func (conf *RecordConfig) getRecorderConfigByType(t string) (recorder *Record) { @@ -95,6 +108,8 @@ func (conf *RecordConfig) getRecorderConfigByType(t string) (recorder *Record) { recorder = &conf.Hls case "raw": recorder = &conf.Raw + case "raw_audio": + recorder = &conf.RawAudio } return } diff --git a/raw.go b/raw.go index 2c04eae..45c0fae 100644 --- a/raw.go +++ b/raw.go @@ -12,11 +12,19 @@ import ( type RawRecorder struct { Recorder + IsAudio bool } func (r *RawRecorder) Start(streamPath string) error { - r.Record = &RecordPluginConfig.Raw + if r.IsAudio { + r.Record = &RecordPluginConfig.RawAudio + } else { + r.Record = &RecordPluginConfig.Raw + } r.ID = streamPath + "/raw" + if r.IsAudio { + r.ID += "_audio" + } if _, ok := RecordPluginConfig.recordings.Load(r.ID); ok { return ErrRecordExist } @@ -24,11 +32,22 @@ func (r *RawRecorder) Start(streamPath string) error { } func (r *RawRecorder) OnEvent(event any) { - r.Recorder.OnEvent(event) switch v := event.(type) { case *RawRecorder: + filename := strconv.FormatInt(time.Now().Unix(), 10) + r.Ext + 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) + } go r.start() case *track.Video: + if r.IsAudio { + break + } if r.Ext == "." { if v.CodecID == codec.CodecID_H264 { r.Ext = ".h264" @@ -36,14 +55,45 @@ func (r *RawRecorder) OnEvent(event any) { r.Ext = ".h265" } } + r.AddTrack(v) + case *track.Audio: + if !r.IsAudio { + break + } + if r.Ext == "." { + switch v.CodecID { + case codec.CodecID_AAC: + r.Ext = ".aac" + case codec.CodecID_PCMA: + r.Ext = ".pcma" + case codec.CodecID_PCMU: + r.Ext = ".pcmu" + } + } + r.AddTrack(v) + case AudioFrame: + if r.Fragment > 0 { + 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) case VideoFrame: - if r.Fragment != 0 && r.newFile { - r.newFile = false - r.Close() - if file, err := r.CreateFileFn(filepath.Join(r.Stream.Path, strconv.FormatInt(time.Now().Unix(), 10)+r.Ext), false); err == nil { - r.SetIO(file) + if r.Fragment > 0 && v.IFrame { + 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) + default: + r.IO.OnEvent(v) } } diff --git a/restful.go b/restful.go index 6a4af12..1f3ff15 100644 --- a/restful.go +++ b/restful.go @@ -3,8 +3,10 @@ package record import ( "encoding/json" "net/http" + "time" . "m7s.live/engine/v4" + "m7s.live/engine/v4/util" ) func (conf *RecordConfig) API_list(w http.ResponseWriter, r *http.Request) { @@ -14,7 +16,7 @@ func (conf *RecordConfig) API_list(w http.ResponseWriter, r *http.Request) { var err error recorder := conf.getRecorderConfigByType(t) if recorder == nil { - for _, t = range []string{"flv", "mp4", "hls", "raw"} { + for _, t = range []string{"flv", "mp4", "hls", "raw", "rawAudio"} { recorder = conf.getRecorderConfigByType(t) var fs []*VideoFileInfo if fs, err = recorder.Tree(recorder.Path, 0); err == nil { @@ -68,6 +70,12 @@ func (conf *RecordConfig) API_start(w http.ResponseWriter, r *http.Request) { recorder.append = query.Get("append") != "" err = recorder.Start(streamPath) id = recorder.ID + case "raw_audio": + var recorder RawRecorder + recorder.IsAudio = true + recorder.append = query.Get("append") != "" + err = recorder.Start(streamPath) + id = recorder.ID default: http.Error(w, "type not supported", http.StatusBadRequest) return @@ -80,14 +88,13 @@ func (conf *RecordConfig) API_start(w http.ResponseWriter, r *http.Request) { } func (conf *RecordConfig) API_list_recording(w http.ResponseWriter, r *http.Request) { - var recordings []any - conf.recordings.Range(func(key, value any) bool { - recordings = append(recordings, value) - return true - }) - if err := json.NewEncoder(w).Encode(recordings); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + util.ReturnJson(func() (recordings []any) { + conf.recordings.Range(func(key, value any) bool { + recordings = append(recordings, value) + return true + }) + return + }, time.Second, w, r) } func (conf *RecordConfig) API_stop(w http.ResponseWriter, r *http.Request) {