Files
plugin-record/main.go
2022-10-11 19:15:02 +08:00

196 lines
4.3 KiB
Go

package record
import (
"encoding/json"
"io"
"net/http"
"sync"
. "m7s.live/engine/v4"
"m7s.live/engine/v4/codec"
"m7s.live/engine/v4/config"
"m7s.live/engine/v4/util"
)
type RecordConfig struct {
config.Subscribe
Flv Record
Mp4 Record
Hls Record
Raw Record
recordings sync.Map
}
var recordConfig = &RecordConfig{
Subscribe: config.Subscribe{
SubAudio: true,
SubVideo: true,
LiveMode: false,
IFrameOnly: false,
WaitTimeout: 10,
},
Flv: Record{
Path: "record/flv",
Ext: ".flv",
GetDurationFn: getFLVDuration,
},
Mp4: Record{
Path: "record/mp4",
Ext: ".mp4",
},
Hls: Record{
Path: "record/hls",
Ext: ".m3u8",
},
Raw: Record{
Path: "record/raw",
Ext: ".", // 默认h264扩展名为.h264,h265扩展名为.h265
},
}
var plugin = InstallPlugin(recordConfig)
func (conf *RecordConfig) OnEvent(event any) {
switch v := event.(type) {
case FirstConfig, config.Config:
conf.Flv.Init()
conf.Mp4.Init()
conf.Hls.Init()
conf.Raw.Init()
case SEpublish:
if conf.Flv.NeedRecord(v.Stream.Path) {
var flv FLVRecorder
flv.Start(v.Stream.Path)
}
if conf.Mp4.NeedRecord(v.Stream.Path) {
NewMP4Recorder().Start(v.Stream.Path)
}
if conf.Hls.NeedRecord(v.Stream.Path) {
var hls HLSRecorder
hls.Start(v.Stream.Path)
}
if conf.Raw.NeedRecord(v.Stream.Path) {
var raw RawRecorder
raw.Start(v.Stream.Path)
}
}
}
func (conf *RecordConfig) getRecorderConfigByType(t string) (recorder *Record) {
switch t {
case "flv":
recorder = &conf.Flv
case "mp4":
recorder = &conf.Mp4
case "hls":
recorder = &conf.Hls
case "raw":
recorder = &conf.Raw
}
return
}
func (conf *RecordConfig) API_list(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
t := query.Get("type")
var files []*VideoFileInfo
var err error
recorder := conf.getRecorderConfigByType(t)
if recorder == nil {
for _, t = range []string{"flv", "mp4", "hls", "raw"} {
recorder = conf.getRecorderConfigByType(t)
var fs []*VideoFileInfo
if fs, err = recorder.Tree(recorder.Path, 0); err == nil {
files = append(files, fs...)
}
}
} else {
files, err = recorder.Tree(recorder.Path, 0)
}
if err == nil {
var bytes []byte
if bytes, err = json.Marshal(files); err == nil {
w.Write(bytes)
}
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (conf *RecordConfig) API_start(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
streamPath := query.Get("streamPath")
if streamPath == "" {
http.Error(w, "no streamPath", http.StatusBadRequest)
return
}
t := query.Get("type")
var sub ISubscriber
var filePath string
var err error
switch t {
case "":
t = "flv"
fallthrough
case "flv":
var flvRecoder FLVRecorder
flvRecoder.append = query.Get("append") != "" && util.Exist(filePath)
err = flvRecoder.Start(streamPath)
case "mp4":
err = NewMP4Recorder().Start(streamPath)
case "hls":
var recorder HLSRecorder
err = recorder.Start(streamPath)
case "raw":
var recorder RawRecorder
recorder.append = query.Get("append") != "" && util.Exist(filePath)
err = recorder.Start(streamPath)
default:
http.Error(w, "type not supported", http.StatusBadRequest)
return
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write([]byte(sub.GetIO().ID))
}
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)
}
}
func (conf *RecordConfig) API_stop(w http.ResponseWriter, r *http.Request) {
if recorder, ok := conf.recordings.Load(r.URL.Query().Get("id")); ok {
recorder.(ISubscriber).Stop()
w.Write([]byte("ok"))
return
}
http.Error(w, "no such recorder", http.StatusBadRequest)
}
func getFLVDuration(file io.ReadSeeker) uint32 {
_, err := file.Seek(-4, io.SeekEnd)
if err == nil {
var tagSize uint32
if tagSize, err = util.ReadByteToUint32(file, true); err == nil {
_, err = file.Seek(-int64(tagSize)-4, io.SeekEnd)
if err == nil {
_, timestamp, _, err := codec.ReadFLVTag(file)
if err == nil {
return timestamp
}
}
}
}
return 0
}