mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-12-24 13:48:04 +08:00
fix: download hls recordtype 'ts' not 'hls',mp4 record save pps/sps before idr
This commit is contained in:
@@ -65,7 +65,7 @@ func (plugin *HLSPlugin) queryRecordStreams(params *requestParams) ([]m7s.Record
|
||||
var recordStreams []m7s.RecordStream
|
||||
|
||||
// 首先查询HLS记录 (ts)
|
||||
query := plugin.DB.Model(&m7s.RecordStream{}).Where("stream_path = ? AND type = ?", params.streamPath, "hls")
|
||||
query := plugin.DB.Model(&m7s.RecordStream{}).Where("stream_path = ? AND type = ?", params.streamPath, "ts")
|
||||
|
||||
// 添加时间范围查询条件
|
||||
if !params.startTime.IsZero() && !params.endTime.IsZero() {
|
||||
@@ -143,7 +143,7 @@ func (plugin *HLSPlugin) hasOnlyMp4Records(fileInfoList []*fileInfo) bool {
|
||||
}
|
||||
|
||||
for _, info := range fileInfoList {
|
||||
if info.recordType == "hls" {
|
||||
if info.recordType == "ts" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func (plugin *HLSPlugin) filterTsFiles(fileInfoList []*fileInfo) []*fileInfo {
|
||||
var filteredList []*fileInfo
|
||||
|
||||
for _, info := range fileInfoList {
|
||||
if info.recordType == "hls" {
|
||||
if info.recordType == "ts" {
|
||||
filteredList = append(filteredList, info)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ func (r *Recorder) Run() (err error) {
|
||||
}
|
||||
return
|
||||
}, func(video *mpegts.VideoFrame) (err error) {
|
||||
r.Event.Duration = uint32(video.Timestamp.Milliseconds() - r.lastTs.Milliseconds())
|
||||
vr := r.RecordJob.Subscriber.VideoReader
|
||||
if vr.Value.IDR {
|
||||
if err = r.writeSegment(video.Timestamp, vr.Value.WriteTime); err != nil {
|
||||
|
||||
@@ -305,34 +305,18 @@ func (p *MP4Plugin) download(w http.ResponseWriter, r *http.Request) {
|
||||
// 首次添加音频轨道
|
||||
addAudioTrack(track)
|
||||
} else if !bytes.Equal(lastAudioTrack.ExtraData, trackExtraData) {
|
||||
// 音频编码参数发生变化,检查是否已存在相同参数的轨道
|
||||
for _, history := range audioHistory {
|
||||
if bytes.Equal(history.ExtraData, trackExtraData) {
|
||||
// 找到相同参数的轨道,重用它
|
||||
audioTrack = history.Track
|
||||
audioTrack.Samplelist = audioHistory[len(audioHistory)-1].Track.Samplelist
|
||||
return
|
||||
}
|
||||
}
|
||||
// 创建新的音频轨道
|
||||
addAudioTrack(track)
|
||||
// 音频编码参数发生变化,不再创建新轨道,直接重用最后一个轨道
|
||||
audioTrack = lastAudioTrack.Track
|
||||
audioTrack.Samplelist = lastAudioTrack.Track.Samplelist
|
||||
}
|
||||
} else if track.Cid.IsVideo() {
|
||||
if lastVideoTrack == nil {
|
||||
// 首次添加视频轨道
|
||||
addVideoTrack(track)
|
||||
} else if !bytes.Equal(lastVideoTrack.ExtraData, trackExtraData) {
|
||||
// 视频编码参数发生变化,检查是否已存在相同参数的轨道
|
||||
for _, history := range videoHistory {
|
||||
if bytes.Equal(history.ExtraData, trackExtraData) {
|
||||
// 找到相同参数的轨道,重用它
|
||||
videoTrack = history.Track
|
||||
videoTrack.Samplelist = videoHistory[len(videoHistory)-1].Track.Samplelist
|
||||
return
|
||||
}
|
||||
}
|
||||
// 创建新的视频轨道
|
||||
addVideoTrack(track)
|
||||
// 视频编码参数发生变化,不再创建新轨道,直接重用最后一个轨道
|
||||
videoTrack = lastVideoTrack.Track
|
||||
videoTrack.Samplelist = lastVideoTrack.Track.Samplelist
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,7 +339,7 @@ func (p *MP4Plugin) download(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
trackCount := len(demuxer.Tracks)
|
||||
//trackCount := len(demuxer.Tracks)
|
||||
|
||||
// 处理轨道信息
|
||||
if i == 0 || flag == mp4.FLAG_FRAGMENT {
|
||||
@@ -365,14 +349,6 @@ func (p *MP4Plugin) download(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查轨道数量是否发生变化
|
||||
if trackCount != len(muxer.Tracks) {
|
||||
if flag == mp4.FLAG_FRAGMENT {
|
||||
// 分片模式下重新生成 MOOV box
|
||||
moov = muxer.MakeMoov()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理开始时间偏移(仅第一个文件)
|
||||
if i == 0 {
|
||||
startTimestamp := startTime.Sub(stream.StartTime).Milliseconds()
|
||||
@@ -491,6 +467,15 @@ func (p *MP4Plugin) download(w http.ResponseWriter, r *http.Request) {
|
||||
part.Close()
|
||||
}
|
||||
} else {
|
||||
|
||||
// 检查轨道数量是否发生变化
|
||||
//if trackCount != len(muxer.Tracks) {
|
||||
if flag == mp4.FLAG_FRAGMENT {
|
||||
// 分片模式下重新生成 MOOV box
|
||||
moov = muxer.MakeMoov()
|
||||
}
|
||||
//}
|
||||
|
||||
// 分片 MP4 模式:输出分片格式
|
||||
var children []box.IBox
|
||||
var totalSize uint64
|
||||
|
||||
@@ -14,7 +14,10 @@ import (
|
||||
"m7s.live/v5/pkg/codec"
|
||||
"m7s.live/v5/pkg/config"
|
||||
"m7s.live/v5/pkg/storage"
|
||||
"m7s.live/v5/pkg/util"
|
||||
"m7s.live/v5/plugin/mp4/pkg/box"
|
||||
|
||||
"github.com/langhuihui/gomem"
|
||||
)
|
||||
|
||||
type WriteTrailerQueueTask struct {
|
||||
@@ -121,8 +124,9 @@ func NewRecorder(conf config.Record) m7s.IRecorder {
|
||||
|
||||
type Recorder struct {
|
||||
m7s.DefaultRecorder
|
||||
muxer *Muxer
|
||||
file storage.File
|
||||
muxer *Muxer
|
||||
file storage.File
|
||||
firstVideoFrame bool // 标记是否是第一个视频帧
|
||||
}
|
||||
|
||||
func (r *Recorder) writeTailer(end time.Time) {
|
||||
@@ -176,6 +180,7 @@ func (r *Recorder) createStream(start time.Time) (err error) {
|
||||
r.muxer = NewMuxerWithStreamPath(0, r.Event.StreamPath)
|
||||
}
|
||||
|
||||
r.firstVideoFrame = true // 重置第一个视频帧标志
|
||||
return r.muxer.WriteInitSegment(r.file)
|
||||
}
|
||||
|
||||
@@ -302,43 +307,98 @@ func (r *Recorder) Run() (err error) {
|
||||
track.ICodecCtx = video.ICodecCtx
|
||||
}
|
||||
}
|
||||
ctx := video.ICodecCtx.(pkg.IVideoCodecCtx)
|
||||
if videoTrackCtx, ok := videoTrack.ICodecCtx.(pkg.IVideoCodecCtx); ok && videoTrackCtx != ctx {
|
||||
width, height := uint32(ctx.Width()), uint32(ctx.Height())
|
||||
oldWidth, oldHeight := uint32(videoTrackCtx.Width()), uint32(videoTrackCtx.Height())
|
||||
r.Info("ctx changed, restarting recording",
|
||||
"old", fmt.Sprintf("%dx%d", oldWidth, oldHeight),
|
||||
"new", fmt.Sprintf("%dx%d", width, height))
|
||||
r.writeTailer(sub.VideoReader.Value.WriteTime)
|
||||
err = r.createStream(sub.VideoReader.Value.WriteTime)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
at, vt = nil, nil
|
||||
if vr := sub.VideoReader; vr != nil {
|
||||
vr.ResetAbsTime()
|
||||
vt = vr.Track
|
||||
switch video.ICodecCtx.GetBase().(type) {
|
||||
case *codec.H264Ctx:
|
||||
track := r.muxer.AddTrack(box.MP4_CODEC_H264)
|
||||
videoTrack = track
|
||||
track.ICodecCtx = video.ICodecCtx
|
||||
case *codec.H265Ctx:
|
||||
track := r.muxer.AddTrack(box.MP4_CODEC_H265)
|
||||
videoTrack = track
|
||||
track.ICodecCtx = video.ICodecCtx
|
||||
}
|
||||
}
|
||||
if ar := sub.AudioReader; ar != nil {
|
||||
ar.ResetAbsTime()
|
||||
}
|
||||
}
|
||||
//ctx := video.ICodecCtx.(pkg.IVideoCodecCtx)
|
||||
//if videoTrackCtx, ok := videoTrack.ICodecCtx.(pkg.IVideoCodecCtx); ok && videoTrackCtx != ctx {
|
||||
// width, height := uint32(ctx.Width()), uint32(ctx.Height())
|
||||
// oldWidth, oldHeight := uint32(videoTrackCtx.Width()), uint32(videoTrackCtx.Height())
|
||||
// r.Info("ctx changed, restarting recording",
|
||||
// "old", fmt.Sprintf("%dx%d", oldWidth, oldHeight),
|
||||
// "new", fmt.Sprintf("%dx%d", width, height))
|
||||
// r.writeTailer(sub.VideoReader.Value.WriteTime)
|
||||
// err = r.createStream(sub.VideoReader.Value.WriteTime)
|
||||
// if err != nil {
|
||||
// return nil
|
||||
// }
|
||||
// at, vt = nil, nil
|
||||
// if vr := sub.VideoReader; vr != nil {
|
||||
// vr.ResetAbsTime()
|
||||
// vt = vr.Track
|
||||
// switch video.ICodecCtx.GetBase().(type) {
|
||||
// case *codec.H264Ctx:
|
||||
// track := r.muxer.AddTrack(box.MP4_CODEC_H264)
|
||||
// videoTrack = track
|
||||
// track.ICodecCtx = video.ICodecCtx
|
||||
// case *codec.H265Ctx:
|
||||
// track := r.muxer.AddTrack(box.MP4_CODEC_H265)
|
||||
// videoTrack = track
|
||||
// track.ICodecCtx = video.ICodecCtx
|
||||
// }
|
||||
// }
|
||||
// if ar := sub.AudioReader; ar != nil {
|
||||
// ar.ResetAbsTime()
|
||||
// }
|
||||
//}
|
||||
sample := box.Sample{
|
||||
Timestamp: sub.VideoReader.AbsTime,
|
||||
KeyFrame: video.IDR,
|
||||
CTS: video.GetCTS32(),
|
||||
Memory: video.Memory,
|
||||
}
|
||||
// 如果是第一个视频 I 帧,将参数集放在 I 帧前面一起写入
|
||||
//if r.firstVideoFrame && video.IDR {
|
||||
if video.IDR {
|
||||
// 创建包含参数集的 Memory
|
||||
var combinedMemory gomem.Memory
|
||||
var naluSizeLen int = 4
|
||||
var sps, pps, vps []byte
|
||||
|
||||
switch ctx := video.ICodecCtx.GetBase().(type) {
|
||||
case *codec.H264Ctx:
|
||||
naluSizeLen = int(ctx.RecordInfo.LengthSizeMinusOne) + 1
|
||||
sps = ctx.SPS()
|
||||
pps = ctx.PPS()
|
||||
if len(sps) > 0 && len(pps) > 0 {
|
||||
// 写入 SPS
|
||||
sizeBuf := make([]byte, naluSizeLen)
|
||||
util.PutBE(sizeBuf, uint32(len(sps)))
|
||||
combinedMemory.Push(sizeBuf)
|
||||
combinedMemory.Push(sps)
|
||||
// 写入 PPS
|
||||
sizeBuf = make([]byte, naluSizeLen)
|
||||
util.PutBE(sizeBuf, uint32(len(pps)))
|
||||
combinedMemory.Push(sizeBuf)
|
||||
combinedMemory.Push(pps)
|
||||
}
|
||||
case *codec.H265Ctx:
|
||||
naluSizeLen = int(ctx.RecordInfo.LengthSizeMinusOne) + 1
|
||||
vps = ctx.VPS()
|
||||
sps = ctx.SPS()
|
||||
pps = ctx.PPS()
|
||||
if len(vps) > 0 && len(sps) > 0 && len(pps) > 0 {
|
||||
// 写入 VPS
|
||||
sizeBuf := make([]byte, naluSizeLen)
|
||||
util.PutBE(sizeBuf, uint32(len(vps)))
|
||||
combinedMemory.Push(sizeBuf)
|
||||
combinedMemory.Push(vps)
|
||||
// 写入 SPS
|
||||
sizeBuf = make([]byte, naluSizeLen)
|
||||
util.PutBE(sizeBuf, uint32(len(sps)))
|
||||
combinedMemory.Push(sizeBuf)
|
||||
combinedMemory.Push(sps)
|
||||
// 写入 PPS
|
||||
sizeBuf = make([]byte, naluSizeLen)
|
||||
util.PutBE(sizeBuf, uint32(len(pps)))
|
||||
combinedMemory.Push(sizeBuf)
|
||||
combinedMemory.Push(pps)
|
||||
}
|
||||
}
|
||||
// 将原始视频帧数据追加到参数集后面
|
||||
combinedMemory.Push(video.Memory.Buffers...)
|
||||
sample.Memory = combinedMemory
|
||||
r.firstVideoFrame = false
|
||||
} else if r.firstVideoFrame {
|
||||
r.firstVideoFrame = false
|
||||
}
|
||||
return r.muxer.WriteSample(r.file, videoTrack, sample)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user