Skip to content

feat: expose buffered ranges on VideoPlayerState across all platforms - #244

Open
itboy87 wants to merge 3 commits into
kdroidFilter:masterfrom
itboy87:feat/buffered-ranges
Open

feat: expose buffered ranges on VideoPlayerState across all platforms#244
itboy87 wants to merge 3 commits into
kdroidFilter:masterfrom
itboy87:feat/buffered-ranges

Conversation

@itboy87

@itboy87 itboy87 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a cross-platform buffering API to VideoPlayerState, so consumers can draw a buffer
indicator behind the seek bar instead of guessing how much media is ready to play.

Every backend funnels its raw values through a shared normalizeBufferedRanges helper, so
callers get one predictable shape: sorted, non-overlapping, clamped to the duration, with
empty/non-finite entries dropped.

API

New in commonMain:

  • BufferedRange(start, end) — an @Immutable span in seconds, with duration and contains.
  • VideoPlayerState.bufferedRanges: List<BufferedRange> — the buffered spans, sorted and merged.
  • VideoPlayerState.bufferedPercentage: Float — how far the media is buffered ahead of the
    current position, 0f..100f.
  • VideoPlayerState.bufferedSliderPos: Float — the same value on the 0f..1000f scale used by
    sliderPos, so it can be drawn directly under the seek bar.
  • VideoPlayerState.isBufferedRangeSupported: Boolean — whether the backend can report buffering
    at all. Check it first: false means "unknown", not "nothing buffered".
  • List<BufferedRange>.bufferedEndAt(position) / .bufferedPercentageAt(position, duration).

All members have interface defaults (false / emptyList() / 0f), so this is source-compatible
for anyone implementing VideoPlayerState. PreviewableVideoPlayerState gained matching
constructor parameters for previews and tests.

Platform implementations

Platform Source Notes
Android Player.getBufferedPosition ExoPlayer exposes a single contiguous buffer, so at most one range, starting at the playback position
iOS AVPlayerItem.loadedTimeRanges KVO observer, fires as the buffer grows — including while paused
macOS (desktop) AVPlayerItem.loadedTimeRanges New nGetBufferedRanges JNI entry point
Linux GStreamer buffering query New nGetBufferedRanges JNI entry point; local files report the whole duration
Windows — HLS IMFMediaEngine buffered ranges Full multi-range support
Windows — local files whole duration Always available on disk
Windows — progressive network Not supported: the Media Foundation source reader exposes no buffering data, so isBufferedRangeSupported is false
Web (JS / Wasm) HTMLMediaElement.buffered Driven by progress, seeked, loadedmetadata, timeupdate; reset on emptied

Implementation details worth flagging:

  • Buffering is polled on its own job (200 ms) on Android, Linux, macOS and Windows, separate
    from the position-update loop — the buffer keeps filling while playback is paused, but the
    position loop is stopped in that state.
  • Native calls degrade gracefully. The desktop backends catch UnsatisfiedLinkError from
    nGetBufferedRanges and permanently flip isBufferedRangeSupported to false, so a bundled
    native library that predates this change reports "unsupported" instead of throwing on every poll.
    bufferedRangesAvailable is backed by mutableStateOf so that flip recomposes.
  • The JVM DefaultVideoPlayerState forwards all three members explicitly to its platform
    delegate — because they have interface defaults, omitting them would silently report "no
    buffering support".
  • Native ranges cross the JNI boundary as a flat [start0, end0, start1, end1, …] DoubleArray
    capped at 16 ranges, decoded by a shared decodeNativeBufferedRanges helper.
  • Web TimeRanges indexing is guarded, since the browser can mutate the object between the length
    check and the read.

Native changes

New nGetBufferedRanges JNI entry points and their implementations for Linux
(NativeVideoPlayer.c / jni_bridge.c), macOS (NativeVideoPlayer.swift / jni_bridge.c) and
Windows (NativeVideoPlayer.cpp / HLSPlayer.cpp / jni_bridge.cpp). The prebuilt native
libraries need to be rebuilt
for the desktop backends to report ranges; until then they fall
back to "unsupported" rather than failing.

Sample app

The sample's seek bar now draws the buffered ranges as a track behind the slider (the inactive
track was made more translucent so they stay readable), plus a one-line readout under the position
text — e.g. Buffered 42% · 0:00–1:12 — which distinguishes "nothing buffered yet" from "this
backend cannot report buffering".

Tests

BufferedRangeTest (commonTest, 11 tests) covers duration/contains, bufferedEndAt including
gaps and empty lists, percentage clamping and unknown/NaN durations, and normalization: sorting,
merging overlapping and touching ranges, dropping empty and non-finite entries, and clamping to
the duration.

Docs

README_VIDEO.MD gains a Buffered Ranges section with a usage snippet, a property table and
the per-platform support table above, plus a feature-list bullet and a TOC entry.

itboy87 and others added 3 commits August 7, 2026 17:45
Adds a buffering API to VideoPlayerState so consumers can draw a buffer
indicator on the seek bar:

  - bufferedRanges: buffered spans in seconds, sorted and merged
  - bufferedPercentage: buffered ahead of the playhead, 0..100 of duration
  - bufferedSliderPos: the same value on the 0..1000 sliderPos scale
  - isBufferedRangeSupported: whether the backend can report buffering at all

All members have default implementations, so external VideoPlayerState
implementations keep compiling.

Backends:
  - Android: ExoPlayer bufferedPosition/bufferedPercentage (single range)
  - iOS/macOS: AVPlayerItem.loadedTimeRanges (multi-range)
  - Web: HTMLMediaElement.buffered (multi-range)
  - Linux: GStreamer buffering query, percent ranges scaled by duration;
    local files report the whole duration
  - Windows: IMFMediaEngine.GetBuffered for HLS, whole duration for local
    files. Progressive network playback runs on IMFSourceReader, which
    exposes no buffering data, so isBufferedRangeSupported is false there.

Buffering is polled on a dedicated job on every backend since the buffer
keeps filling while playback is paused.

The Windows native API version is bumped to 3; the macOS and Linux bridges
degrade to "unsupported" if loaded against a native library that predates
nGetBufferedRanges.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add a new `bufferedSummary` function to PlayerScreen displaying buffering details beneath the current playback position. This includes the buffered percentage and time ranges to improve visibility of buffering state, especially during testing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant