Skip to content

Native remote HLS (.remoteBypass) leaves clock.bufferedPosition at zero during playback #935

Open

Description

@htrung14

Observed on 7.51.0

On a tvOS arm64 simulator, native remote HLS (videoRoute == .remoteBypass) plays and seeks, but clock.bufferedPosition stays 0. This is distinct from #835, which concerned paused native loopback playback.

An integration test loads a locally served synthetic H.264 fMP4 HLS VOD through the production AetherEngine API, verifies automatic .remoteBypass, starts playback, seeks to 8 seconds, and waits for bufferedPosition > currentTime.

Observed:

  • Seek target 8.0, landing 8.000474041 seconds.
  • Buffer-ahead expectation times out.
  • bufferedPosition == 0.0 at currentTime == 20.215616416.
  • The test retains a strict expected failure for the two buffer assertions; this is not a successful buffer check.

Pinned release: 7.51.0, commit a877941bd3402d5f531edad0cbe4c9b33eefa856. No engine modifications. Physical-device reproduction has not been performed.

Fixture recipe

Starting with a synthetic H.264 test-pattern MP4, generate fMP4 HLS:

ffmpeg -hide_banner -v error -i fixture.mp4 -t 20 -an -c:v copy \
  -f hls -hls_segment_type fmp4 -hls_time 2 -hls_playlist_type vod \
  -hls_fmp4_init_filename init.mp4 -hls_segment_filename 'seg%02d.m4s' native-hls.m3u8

Serve the playlist and segments over local HTTP, load the playlist through the normal automatic route, verify .remoteBypass, then observe clock.currentTime and clock.bufferedPosition during playback and after seeking. Expected: publish the actual contiguous AVPlayer buffered frontier rather than leaving it at zero.

Static comparison with 7.52.0

7.52.0 has not been runtime-tested here. Read-only comparison indicates the same publication gap:

  • loadRemoteHLS sinks wire current/rendered times but do not publish bufferedPosition.
  • refreshNativeBufferedPosition depends on nativeVideoSession; the no-argument refresh exits if it is absent. Remote bypass creates a NativeAVPlayerHost without the loopback nativeVideoSession.

Could the remote-bypass route publish the actual AVPlayer loaded-range frontier, including while paused? We are keeping upstream unchanged rather than duplicating engine buffering state in the app.

Activity

  1. quint-aether commented on Oct 30, 2026

    @quint-aether

    This is related to #835, which was closed. That issue covered paused native loopback playback, while this one is about the remote HLS bypass route. On main, docs/api.md documents clock.bufferedPosition as "never below currentTime". A value of 0 at currentTime == 20.2 does not match that contract. The two code paths named in the report also look the same on main as in 7.52.0. The report has what is needed to reproduce it, so it is ready for review.

    Automated triage by Quint (Claude). A maintainer reads every issue and will follow up.

  2. superuser404notfound commented on Oct 30, 2026

    @superuser404notfound
    Owner

    Thanks for the precise report and the fixture recipe, it reproduced on the first run.

    Fixed on main in 721254e (f3f82fd + a8b638c), not in a release yet.

    Cause: the shared refresh returned early without a loopback nativeVideoSession, and the remote bypass never has one, so clock.bufferedPosition kept its reset value of 0 for the whole session.

    Fix: .remoteBypass now publishes the end of AVPlayer's own loadedTimeRanges range that is contiguous with the playhead (or with the pending seek target while a seek is in flight), clamped to never trail currentTime. It refreshes on every rendered-time tick and on every change of the loaded ranges, which AVFoundation keeps reporting while paused, so the value also moves in a paused session. Disconnected islands are not counted.

    Verification with aetherctl play --native-hls against a 40 s fMP4 HLS VOD built with the recipe above, served over local HTTP:

    before after
    playing buf=0.00 buf=40.00
    paused buf=0.00 buf=40.00
    after a seek to 8 s buf=0.00 buf=40.00

    Loopback serves the whole file at once, so this run shows the value arriving, not its growth over a slow link; a throttled origin or the simulator test with the expected failure removed would cover that part. Keeping the issue open until it is confirmed on your side once a release carries it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: apiEngine areaarea: ioEngine areabugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions