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 表示要保存的文件的根路径,可以使用相对路径或者绝对路径
|
- 配置中的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` 停止录制某个流
|
||||||
|
|
||||||
## 点播功能
|
## 点播功能
|
||||||
|
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) {
|
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,11 +36,11 @@ 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
|
||||||
ftyp *mp4.FtypBox
|
ftyp *mp4.FtypBox
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFMP4Recorder() *FMP4Recorder {
|
func NewFMP4Recorder() *FMP4Recorder {
|
||||||
@@ -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
7
hls.go
@@ -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
7
raw.go
@@ -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:
|
||||||
|
21
restful.go
21
restful.go
@@ -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)
|
||||||
}
|
}
|
||||||
|
@@ -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:
|
||||||
|
Reference in New Issue
Block a user