mirror of
https://github.com/kerberos-io/agent.git
synced 2026-09-17 12:22:55 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36d6591271 | ||
|
|
e8fc4e674b | ||
|
|
011bd9936f | ||
|
|
791add83f9 |
@@ -56,6 +56,7 @@ type MP4 struct {
|
||||
FreeBoxSize int64
|
||||
FragmentStartRawPTS uint64 // Raw PTS for timing when to flush fragments
|
||||
FragmentStartDTS uint64 // Accumulated VideoTotalDuration at fragment start (matches tfdt)
|
||||
LastKeyframeRawPTS uint64 // Raw PTS of the most recently seen keyframe (in any fragment)
|
||||
MoofBoxes int64 // Number of moof boxes in the file
|
||||
MoofBoxSizes []int64 // Sizes of each moof box
|
||||
SegmentDurations []uint64 // Duration of each segment in timescale units
|
||||
@@ -233,6 +234,24 @@ func (mp4 *MP4) flushPendingVideoSample(nextPTS uint64) bool {
|
||||
var duration uint64
|
||||
if nextPTS > 0 && nextPTS > mp4.VideoFullSample.DecodeTime {
|
||||
duration = nextPTS - mp4.VideoFullSample.DecodeTime
|
||||
// Guard against forward PTS jumps (e.g. when looping a source MP4
|
||||
// through virtual-rtsp the upstream ffmpeg may insert a large offset
|
||||
// at the loop boundary, or the RTSP stream may stall briefly).
|
||||
// Without this clamp the sample gets a huge duration which appears
|
||||
// as a discontinuity in the trun/sidx/mvhd and causes browsers
|
||||
// (Video.js / MSE) to abort playback with a "media corruption"
|
||||
// error around the loop boundary.
|
||||
var maxPlausible uint64 = 1000 // 1 second hard ceiling
|
||||
if mp4.LastVideoSampleDTS > 0 && mp4.LastVideoSampleDTS*10 < maxPlausible {
|
||||
maxPlausible = mp4.LastVideoSampleDTS * 10
|
||||
}
|
||||
if duration > maxPlausible {
|
||||
log.Log.Warning(fmt.Sprintf("mp4.flushPendingVideoSample(): video PTS jumped forward (nextPTS=%d, prevDTS=%d, gap=%d ms) - clamping to %d ms (likely source loop/stall discontinuity)", nextPTS, mp4.VideoFullSample.DecodeTime, duration, maxPlausible))
|
||||
duration = mp4.LastVideoSampleDTS
|
||||
if duration == 0 {
|
||||
duration = 33
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No valid nextPTS (Close case) or PTS went backwards (jitter/discontinuity)
|
||||
if nextPTS > 0 {
|
||||
@@ -289,6 +308,27 @@ func (mp4 *MP4) AddSampleToTrack(trackID uint32, isKeyframe bool, data []byte, p
|
||||
}
|
||||
shouldFlush := !mp4.Start || elapsed >= FragmentDurationMs
|
||||
|
||||
// Detect upstream source-loop / restart discontinuity. When an MP4 is
|
||||
// looped through virtual-rtsp (ffmpeg `-stream_loop -1 -re`) the loop
|
||||
// seam emits a fresh IDR much sooner than a normal GOP would. PTS keeps
|
||||
// growing monotonically, so the timing-only `elapsed` check above does
|
||||
// not catch it and the seam IDR ends up as a mid-fragment sync sample.
|
||||
// MSE-based players (Video.js / Chromium / Firefox) reject the resulting
|
||||
// fragment with a "media corruption" error because the inner IDR resets
|
||||
// frame_num/POC inside what they expect to be a single GOP. Force a
|
||||
// fragment boundary whenever two consecutive keyframes arrive much
|
||||
// closer than a normal GOP (here: < 500 ms apart). This isolates the
|
||||
// seam IDR into its own fragment so each fragment stays a clean GOP.
|
||||
if !shouldFlush && trackID == uint32(mp4.VideoTrack) && mp4.Start &&
|
||||
mp4.LastKeyframeRawPTS > 0 && pts > mp4.LastKeyframeRawPTS &&
|
||||
pts-mp4.LastKeyframeRawPTS < 500 {
|
||||
log.Log.Warning(fmt.Sprintf("mp4.AddSampleToTrack(): forcing fragment flush at unexpectedly close keyframe (gap=%d ms, fragment elapsed=%d ms) - likely upstream loop/restart discontinuity", pts-mp4.LastKeyframeRawPTS, elapsed))
|
||||
shouldFlush = true
|
||||
}
|
||||
if trackID == uint32(mp4.VideoTrack) {
|
||||
mp4.LastKeyframeRawPTS = pts
|
||||
}
|
||||
|
||||
if shouldFlush {
|
||||
// Write the previous segment to the file
|
||||
if mp4.Start {
|
||||
@@ -393,6 +433,14 @@ func (mp4 *MP4) AddSampleToTrack(trackID uint32, isKeyframe bool, data []byte, p
|
||||
if started {
|
||||
dts = 1
|
||||
}
|
||||
// Guard against forward PTS jumps (e.g. virtual-rtsp loop
|
||||
// boundary or upstream stalls). Without this clamp the
|
||||
// audio trun would carry an enormous sample duration that
|
||||
// renders the recording unplayable in browsers.
|
||||
if mp4.LastAudioSampleDTS > 0 && dts > mp4.LastAudioSampleDTS*10 {
|
||||
log.Log.Warning(fmt.Sprintf("mp4.AddSampleToTrack(): audio PTS jumped forward (pts=%d, prevDTS=%d, gap=%d) - clamping to last known duration", pts, mp4.AudioFullSample.DecodeTime, dts))
|
||||
dts = mp4.LastAudioSampleDTS
|
||||
}
|
||||
mp4.LastAudioSampleDTS = dts
|
||||
//fmt.Printf("Adding sample to track %d, PTS: %d, Duration: %d, size: %d\n", trackID, pts, dts, len(aac[7:]))
|
||||
mp4.AudioTotalDuration += dts
|
||||
|
||||
Reference in New Issue
Block a user