mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-12-24 13:48:04 +08:00
- Refactor frame converter implementation - Update mp4 track to use ICodex - General refactoring and code improvements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
129 lines
3.4 KiB
Go
129 lines
3.4 KiB
Go
package mp4
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"strings"
|
|
"time"
|
|
|
|
m7s "m7s.live/v5"
|
|
pkg "m7s.live/v5/pkg"
|
|
"m7s.live/v5/pkg/util"
|
|
)
|
|
|
|
type HTTPReader struct {
|
|
m7s.HTTPFilePuller
|
|
}
|
|
|
|
func (p *HTTPReader) Run() (err error) {
|
|
pullJob := &p.PullJob
|
|
|
|
// Move to parsing step
|
|
pullJob.GoToStepConst(pkg.StepParsing)
|
|
publisher := pullJob.Publisher
|
|
if publisher == nil {
|
|
io.Copy(io.Discard, p.ReadCloser)
|
|
return
|
|
}
|
|
allocator := util.NewScalableMemoryAllocator(1 << util.MinPowerOf2)
|
|
var demuxer *Demuxer
|
|
defer allocator.Recycle()
|
|
switch v := p.ReadCloser.(type) {
|
|
case io.ReadSeeker:
|
|
demuxer = NewDemuxer(v)
|
|
default:
|
|
var content []byte
|
|
content, err = io.ReadAll(p.ReadCloser)
|
|
demuxer = NewDemuxer(strings.NewReader(string(content)))
|
|
}
|
|
if err = demuxer.Demux(); err != nil {
|
|
return
|
|
}
|
|
publisher.OnSeek = func(seekTime time.Time) {
|
|
p.Stop(errors.New("seek"))
|
|
pullJob.Connection.Args.Set(util.StartKey, seekTime.Local().Format(util.LocalTimeFormat))
|
|
newHTTPReader := &HTTPReader{}
|
|
pullJob.AddTask(newHTTPReader)
|
|
}
|
|
if pullJob.Connection.Args.Get(util.StartKey) != "" {
|
|
seekTime, _ := time.Parse(util.LocalTimeFormat, pullJob.Connection.Args.Get(util.StartKey))
|
|
demuxer.SeekTime(uint64(seekTime.UnixMilli()))
|
|
}
|
|
var writer m7s.PublishWriter[*AudioFrame, *VideoFrame]
|
|
|
|
for _, track := range demuxer.Tracks {
|
|
if track.Cid.IsAudio() {
|
|
writer.PublishAudioWriter = m7s.NewPublishAudioWriter[*AudioFrame](publisher, allocator)
|
|
writer.AudioFrame.ICodecCtx = track.ICodecCtx
|
|
} else {
|
|
writer.PublishVideoWriter = m7s.NewPublishVideoWriter[*VideoFrame](publisher, allocator)
|
|
writer.VideoFrame.ICodecCtx = track.ICodecCtx
|
|
}
|
|
}
|
|
|
|
// Move to streaming step
|
|
pullJob.GoToStepConst(pkg.StepStreaming)
|
|
|
|
// 计算最大时间戳用于累计偏移
|
|
var maxTimestamp uint64
|
|
for track, sample := range demuxer.ReadSample {
|
|
timestamp := uint64(sample.Timestamp) * 1000 / uint64(track.Timescale)
|
|
if timestamp > maxTimestamp {
|
|
maxTimestamp = timestamp
|
|
}
|
|
}
|
|
var timestampOffset uint64
|
|
loop := p.PullJob.Loop
|
|
for {
|
|
demuxer.ReadSampleIdx = make([]uint32, len(demuxer.Tracks))
|
|
for track, sample := range demuxer.ReadSample {
|
|
if p.IsStopped() {
|
|
return
|
|
}
|
|
if _, err = demuxer.reader.Seek(sample.Offset, io.SeekStart); err != nil {
|
|
pullJob.Fail(err.Error())
|
|
return
|
|
}
|
|
fixTimestamp := uint32(uint64(sample.Timestamp)*1000/uint64(track.Timescale) + timestampOffset)
|
|
if track.Cid.IsAudio() {
|
|
if _, err = io.ReadFull(demuxer.reader, writer.AudioFrame.NextN(sample.Size)); err != nil {
|
|
writer.AudioFrame.Recycle()
|
|
pullJob.Fail(err.Error())
|
|
return
|
|
}
|
|
writer.AudioFrame.ICodecCtx = track.ICodecCtx
|
|
writer.AudioFrame.SetTS32(fixTimestamp)
|
|
err = writer.NextAudio()
|
|
if err != nil {
|
|
pullJob.Fail(err.Error())
|
|
return
|
|
}
|
|
} else {
|
|
if _, err = io.ReadFull(demuxer.reader, writer.VideoFrame.NextN(sample.Size)); err != nil {
|
|
writer.VideoFrame.Recycle()
|
|
pullJob.Fail(err.Error())
|
|
return
|
|
}
|
|
writer.VideoFrame.ICodecCtx = track.ICodecCtx
|
|
writer.VideoFrame.SetTS32(fixTimestamp)
|
|
writer.VideoFrame.CTS = time.Duration(sample.CTS) * time.Millisecond
|
|
writer.VideoFrame.IDR = sample.KeyFrame
|
|
err = writer.NextVideo()
|
|
if err != nil {
|
|
pullJob.Fail(err.Error())
|
|
return
|
|
}
|
|
}
|
|
}
|
|
if loop >= 0 {
|
|
loop--
|
|
if loop == -1 {
|
|
break
|
|
}
|
|
}
|
|
// 每次循环后累计时间戳偏移,确保下次循环的时间戳是递增的
|
|
timestampOffset += maxTimestamp + 1
|
|
}
|
|
return
|
|
}
|