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
|
var recordStreams []m7s.RecordStream
|
||||||
|
|
||||||
// 首先查询HLS记录 (ts)
|
// 首先查询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() {
|
if !params.startTime.IsZero() && !params.endTime.IsZero() {
|
||||||
@@ -143,7 +143,7 @@ func (plugin *HLSPlugin) hasOnlyMp4Records(fileInfoList []*fileInfo) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, info := range fileInfoList {
|
for _, info := range fileInfoList {
|
||||||
if info.recordType == "hls" {
|
if info.recordType == "ts" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ func (plugin *HLSPlugin) filterTsFiles(fileInfoList []*fileInfo) []*fileInfo {
|
|||||||
var filteredList []*fileInfo
|
var filteredList []*fileInfo
|
||||||
|
|
||||||
for _, info := range fileInfoList {
|
for _, info := range fileInfoList {
|
||||||
if info.recordType == "hls" {
|
if info.recordType == "ts" {
|
||||||
filteredList = append(filteredList, info)
|
filteredList = append(filteredList, info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ func (r *Recorder) Run() (err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}, func(video *mpegts.VideoFrame) (err error) {
|
}, func(video *mpegts.VideoFrame) (err error) {
|
||||||
|
r.Event.Duration = uint32(video.Timestamp.Milliseconds() - r.lastTs.Milliseconds())
|
||||||
vr := r.RecordJob.Subscriber.VideoReader
|
vr := r.RecordJob.Subscriber.VideoReader
|
||||||
if vr.Value.IDR {
|
if vr.Value.IDR {
|
||||||
if err = r.writeSegment(video.Timestamp, vr.Value.WriteTime); err != nil {
|
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)
|
addAudioTrack(track)
|
||||||
} else if !bytes.Equal(lastAudioTrack.ExtraData, trackExtraData) {
|
} else if !bytes.Equal(lastAudioTrack.ExtraData, trackExtraData) {
|
||||||
// 音频编码参数发生变化,检查是否已存在相同参数的轨道
|
// 音频编码参数发生变化,不再创建新轨道,直接重用最后一个轨道
|
||||||
for _, history := range audioHistory {
|
audioTrack = lastAudioTrack.Track
|
||||||
if bytes.Equal(history.ExtraData, trackExtraData) {
|
audioTrack.Samplelist = lastAudioTrack.Track.Samplelist
|
||||||
// 找到相同参数的轨道,重用它
|
|
||||||
audioTrack = history.Track
|
|
||||||
audioTrack.Samplelist = audioHistory[len(audioHistory)-1].Track.Samplelist
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 创建新的音频轨道
|
|
||||||
addAudioTrack(track)
|
|
||||||
}
|
}
|
||||||
} else if track.Cid.IsVideo() {
|
} else if track.Cid.IsVideo() {
|
||||||
if lastVideoTrack == nil {
|
if lastVideoTrack == nil {
|
||||||
// 首次添加视频轨道
|
// 首次添加视频轨道
|
||||||
addVideoTrack(track)
|
addVideoTrack(track)
|
||||||
} else if !bytes.Equal(lastVideoTrack.ExtraData, trackExtraData) {
|
} else if !bytes.Equal(lastVideoTrack.ExtraData, trackExtraData) {
|
||||||
// 视频编码参数发生变化,检查是否已存在相同参数的轨道
|
// 视频编码参数发生变化,不再创建新轨道,直接重用最后一个轨道
|
||||||
for _, history := range videoHistory {
|
videoTrack = lastVideoTrack.Track
|
||||||
if bytes.Equal(history.ExtraData, trackExtraData) {
|
videoTrack.Samplelist = lastVideoTrack.Track.Samplelist
|
||||||
// 找到相同参数的轨道,重用它
|
|
||||||
videoTrack = history.Track
|
|
||||||
videoTrack.Samplelist = videoHistory[len(videoHistory)-1].Track.Samplelist
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 创建新的视频轨道
|
|
||||||
addVideoTrack(track)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,7 +339,7 @@ func (p *MP4Plugin) download(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
trackCount := len(demuxer.Tracks)
|
//trackCount := len(demuxer.Tracks)
|
||||||
|
|
||||||
// 处理轨道信息
|
// 处理轨道信息
|
||||||
if i == 0 || flag == mp4.FLAG_FRAGMENT {
|
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 {
|
if i == 0 {
|
||||||
startTimestamp := startTime.Sub(stream.StartTime).Milliseconds()
|
startTimestamp := startTime.Sub(stream.StartTime).Milliseconds()
|
||||||
@@ -491,6 +467,15 @@ func (p *MP4Plugin) download(w http.ResponseWriter, r *http.Request) {
|
|||||||
part.Close()
|
part.Close()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// 检查轨道数量是否发生变化
|
||||||
|
//if trackCount != len(muxer.Tracks) {
|
||||||
|
if flag == mp4.FLAG_FRAGMENT {
|
||||||
|
// 分片模式下重新生成 MOOV box
|
||||||
|
moov = muxer.MakeMoov()
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
// 分片 MP4 模式:输出分片格式
|
// 分片 MP4 模式:输出分片格式
|
||||||
var children []box.IBox
|
var children []box.IBox
|
||||||
var totalSize uint64
|
var totalSize uint64
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import (
|
|||||||
"m7s.live/v5/pkg/codec"
|
"m7s.live/v5/pkg/codec"
|
||||||
"m7s.live/v5/pkg/config"
|
"m7s.live/v5/pkg/config"
|
||||||
"m7s.live/v5/pkg/storage"
|
"m7s.live/v5/pkg/storage"
|
||||||
|
"m7s.live/v5/pkg/util"
|
||||||
"m7s.live/v5/plugin/mp4/pkg/box"
|
"m7s.live/v5/plugin/mp4/pkg/box"
|
||||||
|
|
||||||
|
"github.com/langhuihui/gomem"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WriteTrailerQueueTask struct {
|
type WriteTrailerQueueTask struct {
|
||||||
@@ -121,8 +124,9 @@ func NewRecorder(conf config.Record) m7s.IRecorder {
|
|||||||
|
|
||||||
type Recorder struct {
|
type Recorder struct {
|
||||||
m7s.DefaultRecorder
|
m7s.DefaultRecorder
|
||||||
muxer *Muxer
|
muxer *Muxer
|
||||||
file storage.File
|
file storage.File
|
||||||
|
firstVideoFrame bool // 标记是否是第一个视频帧
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Recorder) writeTailer(end time.Time) {
|
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.muxer = NewMuxerWithStreamPath(0, r.Event.StreamPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.firstVideoFrame = true // 重置第一个视频帧标志
|
||||||
return r.muxer.WriteInitSegment(r.file)
|
return r.muxer.WriteInitSegment(r.file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,43 +307,98 @@ func (r *Recorder) Run() (err error) {
|
|||||||
track.ICodecCtx = video.ICodecCtx
|
track.ICodecCtx = video.ICodecCtx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx := video.ICodecCtx.(pkg.IVideoCodecCtx)
|
//ctx := video.ICodecCtx.(pkg.IVideoCodecCtx)
|
||||||
if videoTrackCtx, ok := videoTrack.ICodecCtx.(pkg.IVideoCodecCtx); ok && videoTrackCtx != ctx {
|
//if videoTrackCtx, ok := videoTrack.ICodecCtx.(pkg.IVideoCodecCtx); ok && videoTrackCtx != ctx {
|
||||||
width, height := uint32(ctx.Width()), uint32(ctx.Height())
|
// width, height := uint32(ctx.Width()), uint32(ctx.Height())
|
||||||
oldWidth, oldHeight := uint32(videoTrackCtx.Width()), uint32(videoTrackCtx.Height())
|
// oldWidth, oldHeight := uint32(videoTrackCtx.Width()), uint32(videoTrackCtx.Height())
|
||||||
r.Info("ctx changed, restarting recording",
|
// r.Info("ctx changed, restarting recording",
|
||||||
"old", fmt.Sprintf("%dx%d", oldWidth, oldHeight),
|
// "old", fmt.Sprintf("%dx%d", oldWidth, oldHeight),
|
||||||
"new", fmt.Sprintf("%dx%d", width, height))
|
// "new", fmt.Sprintf("%dx%d", width, height))
|
||||||
r.writeTailer(sub.VideoReader.Value.WriteTime)
|
// r.writeTailer(sub.VideoReader.Value.WriteTime)
|
||||||
err = r.createStream(sub.VideoReader.Value.WriteTime)
|
// err = r.createStream(sub.VideoReader.Value.WriteTime)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
at, vt = nil, nil
|
// at, vt = nil, nil
|
||||||
if vr := sub.VideoReader; vr != nil {
|
// if vr := sub.VideoReader; vr != nil {
|
||||||
vr.ResetAbsTime()
|
// vr.ResetAbsTime()
|
||||||
vt = vr.Track
|
// vt = vr.Track
|
||||||
switch video.ICodecCtx.GetBase().(type) {
|
// switch video.ICodecCtx.GetBase().(type) {
|
||||||
case *codec.H264Ctx:
|
// case *codec.H264Ctx:
|
||||||
track := r.muxer.AddTrack(box.MP4_CODEC_H264)
|
// track := r.muxer.AddTrack(box.MP4_CODEC_H264)
|
||||||
videoTrack = track
|
// videoTrack = track
|
||||||
track.ICodecCtx = video.ICodecCtx
|
// track.ICodecCtx = video.ICodecCtx
|
||||||
case *codec.H265Ctx:
|
// case *codec.H265Ctx:
|
||||||
track := r.muxer.AddTrack(box.MP4_CODEC_H265)
|
// track := r.muxer.AddTrack(box.MP4_CODEC_H265)
|
||||||
videoTrack = track
|
// videoTrack = track
|
||||||
track.ICodecCtx = video.ICodecCtx
|
// track.ICodecCtx = video.ICodecCtx
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if ar := sub.AudioReader; ar != nil {
|
// if ar := sub.AudioReader; ar != nil {
|
||||||
ar.ResetAbsTime()
|
// ar.ResetAbsTime()
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
sample := box.Sample{
|
sample := box.Sample{
|
||||||
Timestamp: sub.VideoReader.AbsTime,
|
Timestamp: sub.VideoReader.AbsTime,
|
||||||
KeyFrame: video.IDR,
|
KeyFrame: video.IDR,
|
||||||
CTS: video.GetCTS32(),
|
CTS: video.GetCTS32(),
|
||||||
Memory: video.Memory,
|
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)
|
return r.muxer.WriteSample(r.file, videoTrack, sample)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user