Skip to content

Seeking into an unbuffered range leaves time range and time display at the old position until the seek completes #1307

Description

@DiFuks

Summary

When a seek targets a position that is not buffered yet, media-time-range and media-time-display keep showing the pre-seek position for the whole stall, and only jump to the new position once the seek completes. With a burst of seek requests (e.g. holding/repeating a keyboard hotkey) nothing in the UI moves at all until the last seek finishes, which reads as a frozen player.

Steps to reproduce

  1. Play an HLS VOD (mine: 1:38:30) and let it buffer, e.g. up to 2610 s.
  2. While playing, seek to a position past the buffered range — a single mediaseekrequest with detail = 2780, or repeated +5 s hotkey presses.
  3. Watch media-time-range / media-time-display during the buffering stall.

Expected: the UI reflects the requested position immediately; the loading indicator communicates the stall.
Actual: both stay at 2480.9 s until the seek resolves.

Measured event log (media-chrome 4.19.2, hls.js 1.6.15, Chrome 150, macOS) — columns are event | media.currentTime | readyState | timeRange bar position (s) | medialoading:

seeking            2780.89  1  2480.9  0
A:medialoading     2780.89  1  2480.9  1
waiting            2780.89  1  2480.9  1
… 824 ms, no state change, bar frozen at 2480.9 …
A:mediacurrenttime 2780.90  4  2780.9  1
timeupdate         2780.90  4  2780.9  1
seeked             2780.90  4  2780.9  0

824 ms on a fast desktop connection; on mobile/throttled it lasts as long as the fragment takes to load.

Cause

media.currentTime already returns the new position as soon as the seek starts (visible in the log above at the seeking event), but nothing propagates it into the store:

  • state-mediator.jsmediaCurrentTime is only recomputed on mediaEvents: ["timeupdate", "loadedmetadata"], and per spec timeupdate is not fired while a seek is pending.
  • request-map.js — the seek request sets the value but returns nothing, so updateState(undefined) is a no-op and there is no optimistic update:
[MediaUIEvents.MEDIA_SEEK_REQUEST](stateMediator, stateOwners, { detail }) {
  const key = "mediaCurrentTime";
  const value = detail;
  stateMediator[key].set(value, stateOwners);
},

Suggested fix

Return the delta so the UI reflects the requested position right away:

stateMediator[key].set(value, stateOwners);
return { [key]: value };

One caveat worth deciding on: if the browser clamps the requested time (seek outside seekable), the state would be optimistic-but-wrong until the next timeupdate corrects it. Reading stateMediator[key].get(stateOwners) back after the set instead of echoing detail would avoid that.

Happy to open a PR for whichever variant you prefer.

Related

#1306 — same visible symptom (frozen time range) from a different cause: backward seeks under 3% of duration are ignored by RangeAnimation.update. The two are independent; a backward seek into an unbuffered range hits both.

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