mirror of
https://github.com/Monibuca/plugin-record.git
synced 2025-10-06 01:07:00 +08:00
196 lines
4.3 KiB
Go
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
|
|
}
|