Skip to content

vhs-gap-skip loop on Samsung Tizen (for CMAF streams?) #1610

Description

@JeppeTh

I noticed that for some HLS streams on Samsung Tizen (5.0) I experience constant jumps of approx 2 seconds due to a "vhs-gap-skip"-loop.

Mon Jul 20 22:20:13 2026 : playing State:3 Currenttime:65.28'
Mon Jul 20 22:20:15 2026 : canplaythrough State:4 Currenttime:66.904'
Mon Jul 20 22:20:15 2026 : start:0 end:33589.68000000038 l:1 duration:0 State:4 Currenttime:66.904'
Mon Jul 20 22:20:17 2026 : canplaythrough State:4 Currenttime:68.789'
Mon Jul 20 22:20:17 2026 : start:0 end:33593.520000000375 l:1 duration:0 State:4 Currenttime:68.789'
Mon Jul 20 22:20:46 2026 : waiting State:1 Currenttime:98.003'
Mon Jul 20 22:20:46 2026 : usage:vhs-gap-skip State:1 Currenttime:99.873333'
Mon Jul 20 22:20:46 2026 : seeking State:1 Currenttime:99.873333'
Mon Jul 20 22:20:47 2026 : seeked State:4 Currenttime:99.84'
Mon Jul 20 22:20:47 2026 : canplay State:4 Currenttime:99.84'
Mon Jul 20 22:20:47 2026 : playing State:4 Currenttime:99.84'
Mon Jul 20 22:20:47 2026 : canplaythrough State:4 Currenttime:99.84'
Mon Jul 20 22:20:47 2026 : start:0 end:33620.40000000035 l:1 duration:0 State:4 Currenttime:99.84'
Mon Jul 20 22:20:49 2026 : waiting State:1 Currenttime:102.154'
Mon Jul 20 22:20:49 2026 : usage:vhs-gap-skip State:1 Currenttime:103.713333'
Mon Jul 20 22:20:49 2026 : seeking State:1 Currenttime:103.713333'

After some help from chatgpt we could determine that the gap occurs due to
source buffer append fails (quota) -> source buffer remove -> tizen removes the entire fragment -> buffer for current time no longer available -> waiting -> etc

Sun Jul 26 12:36:03 2026 : SOURCEBUFFER APPEND: {"index":0,"bytes":3124166,"before":[{"start":7.68,"end":57.6}]}
Sun Jul 26 12:36:03 2026 : SOURCEBUFFER UPDATEEND (append): {"index":0,"ranges":[{"start":11.52,"end":61.44}]}
SunJul 26 12:36:03 2026 : after updateend(append): [{"index":0,"updating":false,"ranges":[{"start":11.52,"end":61.44}]},{"index":1,"updating":false,"ranges":[{"start":0,"end":65.279}]}]
SunJul 26 12:36:04 2026 : SOURCEBUFFER APPEND: {"index":1,"bytes":61865,"before":[{"start":0,"end":65.279}]}
SunJul 26 12:36:04 2026 : SOURCEBUFFER APPEND: {"index":0,"bytes":3110400,"before":[{"start":11.52,"end":61.44}]}
SunJul 26 12:36:04 2026 : SOURCEBUFFER UPDATEEND (append): {"index":1,"ranges":[{"start":0,"end":69.119}]}
SunJul 26 12:36:04 2026 : after updateend(append): [{"index":0,"updating":true,"ranges":[{"start":15.36,"end":61.44}]},{"index":1,"updating":false,"ranges":[{"start":0,"end":69.119}]}]
SunJul 26 12:36:04 2026 : SOURCEBUFFER UPDATEEND (append): {"index":0,"ranges":[{"start":15.36,"end":65.28}]}
SunJul 26 12:36:04 2026 : after updateend(append): [{"index":0,"updating":false,"ranges":[{"start":15.36,"end":65.28}]},{"index":1,"updating":false,"ranges":[{"start":0,"end":69.119}]}]
SunJul 26 12:36:05 2026 : SOURCEBUFFER APPEND: {"index":0,"bytes":3119193,"before":[{"start":15.36,"end":65.28}]}
SunJul 26 12:36:05 2026 : SOURCEBUFFER UPDATEEND (append): {"index":0,"ranges":[{"start":19.2,"end":69.12}]}
SunJul 26 12:36:05 2026 : after updateend(append): [{"index":0,"updating":false,"ranges":[{"start":19.2,"end":69.12}]},{"index":1,"updating":false,"ranges":[{"start":0,"end":69.119}]}]
SunJul 26 12:36:06 2026 : SOURCEBUFFER APPEND: {"index":1,"bytes":61763,"before":[{"start":0,"end":69.119}]}
SunJul 26 12:36:06 2026 : SOURCEBUFFER UPDATEEND (append): {"index":1,"ranges":[{"start":0,"end":72.959}]}
SunJul 26 12:36:06 2026 : after updateend(append): [{"index":0,"updating":false,"ranges":[{"start":19.2,"end":69.12}]},{"index":1,"updating":false,"ranges":[{"start":0,"end":72.959}]}]
SunJul 26 12:36:06 2026 : SOURCEBUFFER APPEND: {"index":0,"bytes":3149852,"before":[{"start":19.2,"end":69.12}]}
SunJul 26 12:36:06 2026 : SOURCEBUFFER APPEND failed: QuotaExceededError: Failed to execute 'appendBuffer' on 'SourceBuffer': The SourceBuffer is full, and cannot free space to append additional buffers.
SunJul 26 12:36:06 2026 : SOURCEBUFFER REMOVE: {"index":1,"start":0,"end":19.684,"before":[{"start":0,"end":72.959}]}
SunJul 26 12:36:06 2026 : SOURCEBUFFER REMOVE: {"index":0,"start":0,"end":19.684,"before":[{"start":19.2,"end":69.12}]}
SunJul 26 12:36:06 2026 : SOURCEBUFFER UPDATEEND (remove): {"index":1,"ranges":[{"start":19.69,"end":72.959}]}
SunJul 26 12:36:06 2026 : after updateend(remove): [{"index":0,"updating":false,"ranges":[{"start":23.04,"end":69.12}]},{"index":1,"updating":false,"ranges":[{"start":19.69,"end":72.959}]}]
SunJul 26 12:36:06 2026 : SOURCEBUFFER UPDATEEND (remove): {"index":0,"ranges":[{"start":23.04,"end":69.12}]}
SunJul 26 12:36:06 2026 : after updateend(remove): [{"index":0,"updating":false,"ranges":[{"start":23.04,"end":69.12}]},{"index":1,"updating":false,"ranges":[{"start":19.69,"end":72.959}]}]
SunJul 26 12:36:06 2026 : waiting State:1 Currenttime:20.823
SunJul 26 12:36:06 2026 : {"event":"waiting","currentTime":20.823,"readyState":1,"paused":false,"seeking":false,"buffered":[{"start":23.04,"end":69.12}]}
SunJul 26 12:36:06 2026 : usage:vhs-gap-skip State:1 Currenttime:23.073333
SunJul 26 12:36:07 2026 : SOURCEBUFFER waiting: [{"index":0,"updating":false,"ranges":[{"start":23.04,"end":69.12}]},{"index":1,"updating":false,"ranges":[{"start":19.69,"end":72.959}]}]
SunJul 26 12:36:07 2026 : waiting: [{"index":0,"updating":false,"ranges":[{"start":23.04,"end":69.12}]},{"index":1,"updating":false,"ranges":[{"start":19.69,"end":72.959}]}]
SunJul 26 12:36:07 2026 : seeking State:1 Currenttime:23.073333
SunJul 26 12:36:07 2026 : {"event":"seeking","currentTime":23.073333,"readyState":1,"paused":false,"seeking":true,"buffered":[{"start":23.04,"end":69.12}]}

I.e. you can see that VHS requests to remove from 19.2, where Samsung seems to clean the fragment up to 23.04 when CT is 20.823

SunJul 26 12:36:06 2026 : SOURCEBUFFER REMOVE: {"index":0,"start":0,"end":19.684,"before":[{"start":19.2,"end":69.12}]}
SunJul 26 12:36:06 2026 : SOURCEBUFFER UPDATEEND (remove): {"index":0,"ranges":[{"start":23.04,"end":69.12}]}
SunJul 26 12:36:06 2026 : waiting State:1 Currenttime:20.823

It seems implementation only uses a one second margin
https://github.com/videojs/http-streaming/blob/main/src/segment-loader.js#L2537C1-L2538C1

    const currentTime = this.currentTime_();
    // Try to remove as much audio and video as possible to make room for new content
    // before retrying.
    const timeToRemoveUntil = currentTime - MIN_BACK_BUFFER;

Using e.g. a 5 seconds margin seems to make playback smooth - even if it leads to some retry loop for failing appends since the requested removal in my case doesn't lead to any removal until the playback has reached the next fragment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions