fix: fmp4 record error

This commit is contained in:
langhuihui
2023-08-13 14:54:07 +08:00
parent 30bb6ed506
commit 8f33e9b802
6 changed files with 43 additions and 33 deletions

View File

@@ -16,7 +16,7 @@ import (
- 配置中的path 表示要保存的文件的根路径,可以使用相对路径或者绝对路径 - 配置中的path 表示要保存的文件的根路径,可以使用相对路径或者绝对路径
- filter 代表要过滤的StreamPath正则表达式如果不匹配则表示不录制。为空代表不进行过滤 - filter 代表要过滤的StreamPath正则表达式如果不匹配则表示不录制。为空代表不进行过滤
- fragment表示分片大小0代表不分片 - fragment表示分片大小20s代表20秒1m代表1分钟可以组合0代表不分片
```yaml ```yaml
record: record:
@@ -51,7 +51,7 @@ record:
- `/record/api/list/recording` 罗列所有正在录制中的流的信息 - `/record/api/list/recording` 罗列所有正在录制中的流的信息
- `/record/api/list?type=[flv|mp4|hls|raw]` 罗列所有录制的flv|mp4|m3u8|raw文件 - `/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` 停止录制某个流 - `/record/api/stop?id=xxx` 停止录制某个流
## 点播功能 ## 点播功能

16
fmp4.go
View File

@@ -15,7 +15,7 @@ type mediaContext struct {
func (m *mediaContext) push(recoder *FMP4Recorder, dt uint32, dur uint32, data []byte, flags uint32) { func (m *mediaContext) push(recoder *FMP4Recorder, dt uint32, dur uint32, data []byte, flags uint32) {
if m.fragment != nil && dt-m.ts > 1000 { if m.fragment != nil && dt-m.ts > 1000 {
m.fragment.Encode(recoder) m.fragment.Encode(recoder.File)
m.fragment = nil m.fragment = nil
} }
if m.fragment == nil { if m.fragment == nil {
@@ -36,7 +36,7 @@ func (m *mediaContext) push(recoder *FMP4Recorder, dt uint32, dur uint32, data [
type FMP4Recorder struct { type FMP4Recorder struct {
Recorder Recorder
*mp4.InitSegment `json:"-" yaml:"-"` initSegment *mp4.InitSegment `json:"-" yaml:"-"`
video mediaContext video mediaContext
audio mediaContext audio mediaContext
seqNumber uint32 seqNumber uint32
@@ -73,10 +73,10 @@ func (r *FMP4Recorder) OnEvent(event any) {
r.Recorder.OnEvent(event) r.Recorder.OnEvent(event)
switch v := event.(type) { switch v := event.(type) {
case FileWr: case FileWr:
r.InitSegment = mp4.CreateEmptyInit() r.initSegment = mp4.CreateEmptyInit()
r.Moov.Mvhd.NextTrackID = 1 r.initSegment.Moov.Mvhd.NextTrackID = 1
if r.VideoReader != nil { if r.VideoReader != nil {
moov := r.Moov moov := r.initSegment.Moov
trackID := moov.Mvhd.NextTrackID trackID := moov.Mvhd.NextTrackID
moov.Mvhd.NextTrackID++ moov.Mvhd.NextTrackID++
newTrak := mp4.CreateEmptyTrak(trackID, 1000, "video", "chi") newTrak := mp4.CreateEmptyTrak(trackID, 1000, "video", "chi")
@@ -97,7 +97,7 @@ func (r *FMP4Recorder) OnEvent(event any) {
} }
} }
if r.AudioReader != nil { if r.AudioReader != nil {
moov := r.Moov moov := r.initSegment.Moov
trackID := moov.Mvhd.NextTrackID trackID := moov.Mvhd.NextTrackID
moov.Mvhd.NextTrackID++ moov.Mvhd.NextTrackID++
newTrak := mp4.CreateEmptyTrak(trackID, 1000, "audio", "chi") newTrak := mp4.CreateEmptyTrak(trackID, 1000, "audio", "chi")
@@ -128,8 +128,8 @@ func (r *FMP4Recorder) OnEvent(event any) {
stsd.AddChild(pcmu) stsd.AddChild(pcmu)
} }
} }
r.ftyp.Encode(r) r.ftyp.Encode(v)
r.Moov.Encode(r) r.initSegment.Moov.Encode(v)
r.seqNumber = 0 r.seqNumber = 0
case AudioFrame: case AudioFrame:
if r.audio.trackId != 0 { if r.audio.trackId != 0 {

7
hls.go
View File

@@ -32,7 +32,12 @@ func (h *HLSRecorder) Start(streamPath string) error {
h.ID = streamPath + "/hls" h.ID = streamPath + "/hls"
return h.start(h, streamPath, SUBTYPE_RAW) 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) { func (h *HLSRecorder) OnEvent(event any) {
var err error var err error
defer func() { defer func() {

7
raw.go
View File

@@ -30,7 +30,12 @@ func (r *RawRecorder) Start(streamPath string) error {
} }
return r.start(r, streamPath, SUBTYPE_RAW) 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) { func (r *RawRecorder) OnEvent(event any) {
switch v := event.(type) { switch v := event.(type) {
case FileWr: case FileWr:

View File

@@ -2,10 +2,10 @@ package record
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"time" "time"
"go.uber.org/zap"
. "m7s.live/engine/v4" . "m7s.live/engine/v4"
"m7s.live/engine/v4/util" "m7s.live/engine/v4/util"
) )
@@ -74,34 +74,37 @@ func (conf *RecordConfig) API_start(w http.ResponseWriter, r *http.Request) {
} }
recorder := irecorder.GetRecorder() recorder := irecorder.GetRecorder()
if fragment != "" { if fragment != "" {
recorder.Fragment, err = time.ParseDuration(fragment) f, err := time.ParseDuration(fragment)
if err != nil {
recorder.Fragment = f
}
} }
recorder.FileName = fileName recorder.FileName = fileName
recorder.append = query.Get("append") != "" recorder.append = query.Get("append") != ""
err = irecorder.Start(streamPath) err = irecorder.Start(streamPath)
id = recorder.ID id = recorder.ID
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) util.ReturnError(util.APIErrorInternal, err.Error(), w, r)
return return
} }
fmt.Fprintf(w, id) util.ReturnError(util.APIErrorNone, id, w, r)
} }
func (conf *RecordConfig) API_list_recording(w http.ResponseWriter, r *http.Request) { 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 { conf.recordings.Range(func(key, value any) bool {
recordings = append(recordings, value) recordings = append(recordings, value)
return true return true
}) })
return return
}, time.Second, w, r) }, w, r)
} }
func (conf *RecordConfig) API_stop(w http.ResponseWriter, r *http.Request) { func (conf *RecordConfig) API_stop(w http.ResponseWriter, r *http.Request) {
if recorder, ok := conf.recordings.Load(r.URL.Query().Get("id")); ok { if recorder, ok := conf.recordings.Load(r.URL.Query().Get("id")); ok {
recorder.(ISubscriber).Stop() recorder.(ISubscriber).Stop(zap.String("reason", "api"))
w.Write([]byte("ok")) util.ReturnOK(w, r)
return return
} }
http.Error(w, "no such recorder", http.StatusBadRequest) util.ReturnError(util.APIErrorNotFound, "no such recorder", w, r)
} }

View File

@@ -35,13 +35,6 @@ func (r *Recorder) CreateFile() (FileWr, error) {
return r.createFile() 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) { func (r *Recorder) createFile() (f FileWr, err error) {
filePath := r.getFileName(r.Stream.Path) + r.Ext filePath := r.getFileName(r.Stream.Path) + r.Ext
f, err = r.CreateFileFn(filePath, r.append) 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) r.PlayBlock(subType)
RecordPluginConfig.recordings.Delete(r.ID) RecordPluginConfig.recordings.Delete(r.ID)
delete(r.recording, streamPath) delete(r.recording, streamPath)
re.Close()
}() }()
} }
return 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) { func (r *Recorder) OnEvent(event any) {
switch v := event.(type) { switch v := event.(type) {
case IRecorder: case IRecorder: