mirror of
https://github.com/Monibuca/plugin-record.git
synced 2025-10-05 16:56:53 +08:00
fix: fmp4 record error
This commit is contained in:
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
- 配置中的path 表示要保存的文件的根路径,可以使用相对路径或者绝对路径
|
||||
- filter 代表要过滤的StreamPath正则表达式,如果不匹配,则表示不录制。为空代表不进行过滤
|
||||
- fragment表示分片大小(秒),0代表不分片
|
||||
- fragment表示分片大小(20s代表20秒,1m代表1分钟,可以组合),0代表不分片
|
||||
|
||||
```yaml
|
||||
record:
|
||||
@@ -51,7 +51,7 @@ record:
|
||||
|
||||
- `/record/api/list/recording` 罗列所有正在录制中的流的信息
|
||||
- `/record/api/list?type=[flv|mp4|hls|raw]` 罗列所有录制的flv|mp4|m3u8|raw文件
|
||||
- `/record/api/start?type=flv&streamPath=live/rtc&fileName=xxx&fragment=10s` 开始录制某个流,返回一个字符串用于停止录制用的id(fileName是可选的,且只用于非切片情况,fragment用于覆盖配置中的切片时间,是可选的)
|
||||
- `/record/api/start?type=flv&streamPath=live/rtc&fileName=xxx&fragment=10s` 开始录制某个流,返回一个字符串用于停止录制用的id(fileName是可选的,且只用于非切片情况,fragment用于覆盖配置中的切片时间,是可选的,如果fileName和fragment都存在,则忽略fileName)
|
||||
- `/record/api/stop?id=xxx` 停止录制某个流
|
||||
|
||||
## 点播功能
|
||||
|
24
fmp4.go
24
fmp4.go
@@ -15,7 +15,7 @@ type mediaContext struct {
|
||||
|
||||
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.Encode(recoder.File)
|
||||
m.fragment = nil
|
||||
}
|
||||
if m.fragment == nil {
|
||||
@@ -36,11 +36,11 @@ func (m *mediaContext) push(recoder *FMP4Recorder, dt uint32, dur uint32, data [
|
||||
|
||||
type FMP4Recorder struct {
|
||||
Recorder
|
||||
*mp4.InitSegment `json:"-" yaml:"-"`
|
||||
video mediaContext
|
||||
audio mediaContext
|
||||
seqNumber uint32
|
||||
ftyp *mp4.FtypBox
|
||||
initSegment *mp4.InitSegment `json:"-" yaml:"-"`
|
||||
video mediaContext
|
||||
audio mediaContext
|
||||
seqNumber uint32
|
||||
ftyp *mp4.FtypBox
|
||||
}
|
||||
|
||||
func NewFMP4Recorder() *FMP4Recorder {
|
||||
@@ -73,10 +73,10 @@ 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
|
||||
r.initSegment = mp4.CreateEmptyInit()
|
||||
r.initSegment.Moov.Mvhd.NextTrackID = 1
|
||||
if r.VideoReader != nil {
|
||||
moov := r.Moov
|
||||
moov := r.initSegment.Moov
|
||||
trackID := moov.Mvhd.NextTrackID
|
||||
moov.Mvhd.NextTrackID++
|
||||
newTrak := mp4.CreateEmptyTrak(trackID, 1000, "video", "chi")
|
||||
@@ -97,7 +97,7 @@ func (r *FMP4Recorder) OnEvent(event any) {
|
||||
}
|
||||
}
|
||||
if r.AudioReader != nil {
|
||||
moov := r.Moov
|
||||
moov := r.initSegment.Moov
|
||||
trackID := moov.Mvhd.NextTrackID
|
||||
moov.Mvhd.NextTrackID++
|
||||
newTrak := mp4.CreateEmptyTrak(trackID, 1000, "audio", "chi")
|
||||
@@ -128,8 +128,8 @@ func (r *FMP4Recorder) OnEvent(event any) {
|
||||
stsd.AddChild(pcmu)
|
||||
}
|
||||
}
|
||||
r.ftyp.Encode(r)
|
||||
r.Moov.Encode(r)
|
||||
r.ftyp.Encode(v)
|
||||
r.initSegment.Moov.Encode(v)
|
||||
r.seqNumber = 0
|
||||
case AudioFrame:
|
||||
if r.audio.trackId != 0 {
|
||||
|
7
hls.go
7
hls.go
@@ -32,7 +32,12 @@ func (h *HLSRecorder) Start(streamPath string) error {
|
||||
h.ID = streamPath + "/hls"
|
||||
return h.start(h, streamPath, SUBTYPE_RAW)
|
||||
}
|
||||
|
||||
func (r *HLSRecorder) Close() (err error) {
|
||||
if r.File != nil {
|
||||
err = r.File.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
func (h *HLSRecorder) OnEvent(event any) {
|
||||
var err error
|
||||
defer func() {
|
||||
|
7
raw.go
7
raw.go
@@ -30,7 +30,12 @@ func (r *RawRecorder) Start(streamPath string) error {
|
||||
}
|
||||
return r.start(r, streamPath, SUBTYPE_RAW)
|
||||
}
|
||||
|
||||
func (r *RawRecorder) Close() (err error) {
|
||||
if r.File != nil {
|
||||
err = r.File.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
func (r *RawRecorder) OnEvent(event any) {
|
||||
switch v := event.(type) {
|
||||
case FileWr:
|
||||
|
21
restful.go
21
restful.go
@@ -2,10 +2,10 @@ package record
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
. "m7s.live/engine/v4"
|
||||
"m7s.live/engine/v4/util"
|
||||
)
|
||||
@@ -74,34 +74,37 @@ func (conf *RecordConfig) API_start(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
recorder := irecorder.GetRecorder()
|
||||
if fragment != "" {
|
||||
recorder.Fragment, err = time.ParseDuration(fragment)
|
||||
f, err := time.ParseDuration(fragment)
|
||||
if err != nil {
|
||||
recorder.Fragment = f
|
||||
}
|
||||
}
|
||||
recorder.FileName = fileName
|
||||
recorder.append = query.Get("append") != ""
|
||||
err = irecorder.Start(streamPath)
|
||||
id = recorder.ID
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
util.ReturnError(util.APIErrorInternal, err.Error(), w, r)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, id)
|
||||
util.ReturnError(util.APIErrorNone, id, w, r)
|
||||
}
|
||||
|
||||
func (conf *RecordConfig) API_list_recording(w http.ResponseWriter, r *http.Request) {
|
||||
util.ReturnJson(func() (recordings []any) {
|
||||
util.ReturnFetchValue(func() (recordings []any) {
|
||||
conf.recordings.Range(func(key, value any) bool {
|
||||
recordings = append(recordings, value)
|
||||
return true
|
||||
})
|
||||
return
|
||||
}, time.Second, w, r)
|
||||
}, w, r)
|
||||
}
|
||||
|
||||
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"))
|
||||
recorder.(ISubscriber).Stop(zap.String("reason", "api"))
|
||||
util.ReturnOK(w, r)
|
||||
return
|
||||
}
|
||||
http.Error(w, "no such recorder", http.StatusBadRequest)
|
||||
util.ReturnError(util.APIErrorNotFound, "no such recorder", w, r)
|
||||
}
|
||||
|
@@ -35,13 +35,6 @@ 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)
|
||||
@@ -77,7 +70,6 @@ func (r *Recorder) start(re IRecorder, streamPath string, subType byte) (err err
|
||||
r.PlayBlock(subType)
|
||||
RecordPluginConfig.recordings.Delete(r.ID)
|
||||
delete(r.recording, streamPath)
|
||||
re.Close()
|
||||
}()
|
||||
}
|
||||
return
|
||||
@@ -96,6 +88,11 @@ func (r *Recorder) cut(absTime uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// func (r *Recorder) Stop(reason ...zap.Field) {
|
||||
// r.Close()
|
||||
// r.Subscriber.Stop(reason...)
|
||||
// }
|
||||
|
||||
func (r *Recorder) OnEvent(event any) {
|
||||
switch v := event.(type) {
|
||||
case IRecorder:
|
||||
|
Reference in New Issue
Block a user