Files
monibuca/pkg/av_reader.go
2025-09-26 15:57:26 +08:00

191 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package pkg
import (
"context"
"log/slog"
"time"
"github.com/langhuihui/gotask"
"m7s.live/v5/pkg/codec"
"m7s.live/v5/pkg/config"
)
const (
READSTATE_INIT = iota
READSTATE_FIRST
READSTATE_NORMAL
READSTATE_WAITKEY
)
const (
SUBMODE_REAL = iota
SUBMODE_RECORD
SUBMODE_BUFFER
SUBMODE_WAITKEY
)
type AVRingReader struct {
*slog.Logger
RingReader
Track *AVTrack
State byte
FirstSeq uint32
StartTs time.Duration
FirstTs time.Duration
SkipTs time.Duration //ms
beforeJump time.Duration
LastCodecCtx codec.ICodecCtx
startTime time.Time
AbsTime uint32
Delay uint32
BPS uint32 // Bytes per second
}
func (r *AVRingReader) DecConfChanged() bool {
return r.LastCodecCtx != r.Track.ICodecCtx
}
func NewAVRingReader(t *AVTrack, dataType string) *AVRingReader {
t.Debug("create reader")
return &AVRingReader{
Track: t,
Logger: t.With("reader", dataType),
}
}
func (r *AVRingReader) readFrame(mode int) (err error) {
if err = r.ReadNext(); err != nil {
return
}
// 超过一半的缓冲区大小说明Reader太慢需要丢帧
if mode != SUBMODE_BUFFER && r.State == READSTATE_NORMAL && r.Track.LastValue.Sequence-r.Value.Sequence > uint32(r.Track.Size/2) {
if idr := r.Track.GetIDR(); idr != nil && idr.Value.Sequence > r.Value.Sequence {
r.Warn("reader too slow", "lastSeq", r.Track.LastValue.Sequence, "seq", r.Value.Sequence)
return r.Read(idr)
}
}
return
}
func (r *AVRingReader) ReadFrame(conf *config.Subscribe) (err error) {
switch r.State {
case READSTATE_INIT:
r.Info("start read", "mode", conf.SubMode)
startRing := r.Track.Ring
idr := r.Track.GetIDR()
if idr != nil {
startRing = idr
} else if r.Track.ICodecCtx != nil {
r.Warn("no IDRring", "track", r.Track.FourCC().String())
} else {
r.Warn("no codec")
}
switch conf.SubMode {
case SUBMODE_REAL:
if idr != nil {
r.State = READSTATE_FIRST
} else {
r.State = READSTATE_NORMAL
}
case SUBMODE_RECORD:
r.State = READSTATE_NORMAL
case SUBMODE_BUFFER:
for {
currentBft := r.Track.CurrentBufferTime()
if delta := conf.BufferTime - currentBft; delta > 0 {
r.Info("wait buffer", "currentBft", currentBft, "delta", delta)
time.Sleep(delta)
} else {
break
}
}
if idr := r.Track.GetHistoryIDR(conf.BufferTime); idr != nil {
startRing = idr
}
r.State = READSTATE_NORMAL
case SUBMODE_WAITKEY:
startRing = r.Track.Ring
if startRing == r.Track.GetIDR() {
r.State = READSTATE_NORMAL
} else {
r.State = READSTATE_WAITKEY
}
}
if err = r.StartRead(startRing); err != nil {
return
}
r.startTime = time.Now()
if r.FirstTs == 0 {
r.FirstTs = r.Value.Timestamp
}
r.SkipTs = r.FirstTs - r.StartTs
r.FirstSeq = r.Value.Sequence
r.Info("first frame read", "firstTs", r.FirstTs, "firstSeq", r.FirstSeq)
case READSTATE_FIRST:
if idr := r.Track.GetIDR(); idr.Value.Sequence != r.FirstSeq {
if err = r.Read(idr); err != nil {
return
}
r.SkipTs = r.Value.Timestamp - r.beforeJump - r.StartTs - 10*time.Millisecond
r.Info("jump", "skipSeq", idr.Value.Sequence-r.FirstSeq, "skipTs", r.SkipTs)
r.State = READSTATE_NORMAL
} else {
if err = r.readFrame(conf.SubMode); err != nil {
return
}
r.beforeJump = r.Value.Timestamp - r.FirstTs
// 防止过快消费
if fast := r.beforeJump - time.Since(r.startTime); fast > 0 && fast < time.Second {
time.Sleep(fast)
}
}
case READSTATE_NORMAL:
if err = r.readFrame(conf.SubMode); err != nil {
return
}
// if conf.SubMode == SUBMODE_NOJUMP {
// // 防止过快消费
// if fast := r.Value.Timestamp - r.FirstTs - time.Since(r.startTime); fast > 0 && fast < time.Second {
// time.Sleep(fast)
// }
// }
case READSTATE_WAITKEY:
r.Info("wait key frame", "seq", r.Value.Sequence)
for {
if err = r.readFrame(conf.SubMode); err != nil {
return
}
if r.Value.IDR {
r.Info("key frame read", "seq", r.Value.Sequence)
r.State = READSTATE_NORMAL
break
}
}
}
if r.Value.Timestamp < r.SkipTs {
r.Warn("timestamp < skipTs", "ts", r.Value.Timestamp, "skipTs", r.SkipTs, "absTime", r.AbsTime)
r.AbsTime++
} else {
r.AbsTime = uint32((r.Value.Timestamp - r.SkipTs).Milliseconds())
if r.AbsTime == 0 {
r.AbsTime = 1
}
}
r.Delay = r.Track.LastValue.Sequence - r.Value.Sequence
// fmt.Println(r.Delay)
if r.Track.ICodecCtx != nil {
if r.Logger.Enabled(context.TODO(), task.TraceLevel) {
r.Log(context.TODO(), task.TraceLevel, r.Track.FourCC().String(), "ts", r.Value.Timestamp, "delay", r.Delay, "bps", r.BPS)
}
} else {
r.Warn("no codec")
}
return
}
func (r *AVRingReader) ResetAbsTime() {
r.SkipTs = r.Value.Timestamp
r.Info("reset absTime", "skipTs", r.SkipTs)
r.AbsTime = 1
}