Fix catchup stream backward skip on EOF near live edge#374
Fix catchup stream backward skip on EOF near live edge#374kontell wants to merge 4 commits intoxbmc:Piersfrom
Conversation
When a terminating catchup stream hits EOF, the restart used m_previousLiveBufferOffset (set at last seek time) which was stale. This caused playback to jump backward by however far the stream had advanced since the last seek. Use m_currentDemuxTime instead so the stream continues from where playback actually was. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Review Details
|
|
Thank you for the contribution! Great fix, if you would like it released please update the micro version number in addon.xml.in and add a changelog entry. I've also merged your previous PR so feel to also add "Fix 6s close delay on timeshift HLS streams" to the changelog. Let me know. Thanks again. |
|
Thanks for reviewing this. I've updated addon.xml.in & changelog.txt as suggested. |
|
The new commits are good. However you seem to have picked up a merge commit somewhere. There should only be 3 commits and not 4. |
When watching a catchup stream within ~15 minutes of the live edge, playback periodically jumps backward. The skip happens every time the HLS catchup playlist is consumed and FFmpeg returns EOF.
Observed on Flussonic HLS servers which serve catchup playlists as finite VOD (
#EXT-X-PLAYLIST-TYPE:VODwith#EXT-X-ENDLIST). The catchup URL uses&utc={utc}&lutc={lutc}parameters wherelutcis fixed at URL generation time. Once playback reaches the end of the playlist, FFmpeg hits EOF, triggering the addon's restart logic.Fixes: kodi-pvr/pvr.iptvsimple#781
Root cause
The EOF restart handler in
FFmpegCatchupStream::DemuxRead()usedm_previousLiveBufferOffsetto determine where to seek after EOF. This variable is set when the stream opens or when the user seeks, but is never updated during continuous playback.Near the live edge, the catchup VOD playlist is short (only covering the time from
utctolutc). Playback consumes it quickly — within a minute or two — and hits EOF. The restart then seeks back tom_previousLiveBufferOffset(the position at seek time), which is ~1–2 minutes behind the actual playback position. This creates a repeating loop: play briefly, EOF, skip back, play the same content again, EOF, skip back...Fix
Use
m_currentDemuxTimeinstead, which tracks the actual playback position as packets are demuxed. This ensures the EOF restart continues from where playback actually was, regardless of when the last seek occurred.m_currentDemuxTimeandm_pauseStartTimewere both declared without an initializer and not set in the constructor's init list — left as indeterminate values of typedouble. The EOF restart path is gated by!m_isOpeningStream, so it can't be reached before the first packet setsm_currentDemuxTime, but the same isn't true for the pause path that readsm_pauseStartTime. Added= 0initializers to both for safety.m_previousLiveBufferOffset: seconds since catchup buffer start, set once at open/seek, never updated during playback. Represents when the user last interacted, not where playback is.m_currentDemuxTime: milliseconds, updated frompPacket->ptson every demuxed packet. Represents actual playback position.DemuxSeekTime()expects milliseconds, som_currentDemuxTimecan be passed directly (the old code multipliedm_previousLiveBufferOffsetby 1000 to convert from seconds).Testing
Tested on Android (Kodi 22, ARM v7) with a Flussonic HLS catchup stream.
Before fix — backward skip confirmed
currentDemuxTimewas 518426 but the restart seeked to 518405 (m_previousLiveBufferOffset) — a 21-second backward skip. Kodi's video player confirms:After fix — no backward skip
Debug logging over 5 minutes captured 4 consecutive EOF restart cycles:
CVideoPlayerAudio:: synctype set to 0) — brief audio glitch but no video disruptionCVideoPlayer::CheckContinuity - resync backwardmessages (present in the pre-fix log)