LiveProgress

Pure math for the live/DVR scrub bar. Extracted so the epoch-anchored timeshift domain — which took three iterations to stop sawtoothing as the HLS playlist refreshed — is guarded by unit tests instead of only pixel-checked on a device.

The domain grows with real time: domainMs spans [anchor .. live-edge-now], so the thumb keeps advancing while the viewer watches from behind the edge. All inputs are epoch/ms primitives; there is no Media3 or Android dependency.

Functions

Link copied to clipboard
fun atLiveEdge(isLive: Boolean, durationMs: Long, positionMs: Long, thresholdMs: Long): Boolean

True when within thresholdMs of the live edge (or duration unknown).

Link copied to clipboard
fun bufferedFraction(anchorEpochMs: Long, windowStartEpochMs: Long, bufferedMs: Long, domainMs: Long): Float

Buffered edge as a 0..1 fraction of the domain.

Link copied to clipboard
fun domainMs(anchorEpochMs: Long, wallClockMs: Long, latencyMs: Long, durationMs: Long): Long

Growing timeshift domain: window from anchorEpochMs to the current live edge (playhead wall clock + latency), never smaller than the DVR window durationMs.

Link copied to clipboard
fun expiredFraction(domainMs: Long, durationMs: Long): Float

The stretch that has aged out of the DVR window, as a 0..1 fraction.

Link copied to clipboard
fun fraction(anchorEpochMs: Long, playheadWallClockMs: Long, domainMs: Long): Float

Playhead position as a 0..1 fraction of the domain. Uses the smooth epoch wall clock (monotonic across playlist refreshes), NOT the window-relative position that sawtoothed by a segment each refresh.