Skip to content

5.16.0

Choose a tag to compare

@superuser404notfound superuser404notfound released this 21 Jul 04:17

Added

LoadOptions.liveBlockingReload: Bool? (#167)

A host override for LL-HLS blocking-reload on live loopback sessions. nil (default) keeps the automatic, cadence-derived behavior described below; true / false force CAN-BLOCK-RELOAD on or off regardless of observed cadence. The #EXT-X-TARGETDURATION floor still tracks observed cadence either way. Ignored for nativeRemoteHLS and VOD.

Fixed

Live: derive LL-HLS blocking-reload from observed cadence, not the self-reported TARGETDURATION (#167)

Live HLS ingest from a bursty origin looped on CoreMediaErrorDomain -15410 ("Invalid server blocking reload behavior for low latency"): a stall, the engine's own nudge-seek recovery, a brief resume, then the same cycle again.

Root cause. LL-HLS blocking-reload eligibility and the local #EXT-X-TARGETDURATION floor both derived from HLSLiveIngestReader.upstreamTargetDuration, which is the upstream origin's self-declared #EXT-X-TARGETDURATION. A relay / budget IPTV origin that advertises a normal target but pushes segments in irregular batches (rather than encoding in disciplined real time) defeated that check: the advertised value looked fine, so the loopback server kept advertising CAN-BLOCK-RELOAD=YES and held each ?_HLS_msn= reload until the next batch landed, a hold AVPlayer treats as a spec violation (-15410). The hint was also read once at load and frozen as an immutable decision, so it could never reflect how the origin actually delivered.

Fix. The ingest reader now measures the observed inter-segment arrival cadence (LiveArrivalCadenceMeter: the recent-max closed inter-arrival interval, widened by the currently-open gap so a lengthening quiet stretch raises the estimate in real time). Both decisions are re-evaluated from it on every manifest render (LiveCadencePolicy):

  • Blocking-reload gate starts OFF, latches ON only after a sustained window of disciplined cadence, and latches permanently OFF the moment a burst is observed. The path is monotonic OFF → ON → OFF(terminal) on purpose: ON/OFF flapping would itself straddle an in-flight blocking reload and trip -15410, and a source disciplined long enough to earn the ON is very unlikely to burst afterwards. Because the gate starts OFF, a bursty origin never advertises blocking-reload in the first place, so -15410 cannot arise.
  • TARGETDURATION floor is the monotonic max of observed cadence (seeded by the self-reported target, a valid lower bound on segment duration), so AVPlayer's 1.5x-target unchanged-playlist patience always covers the real inter-batch gap. This closes the door on the failure re-manifesting as -12888.

Plain-url: live with no cadence signal, for example a Jellyfin real-time transcode (which is disciplined), keeps blocking-reload on by default, so mainstream live TV is unaffected. Hosts can force either behavior with the new LoadOptions.liveBlockingReload.

The pure pieces are extracted for coverage: LiveArrivalCadenceMeter, the LiveCadencePolicy latch state machine, the override precedence, and the server-manifest shaping (a bursty provider omits CAN-BLOCK-RELOAD and raises #EXT-X-TARGETDURATION; a disciplined one advertises it) are all pinned in LiveCadencePolicyTests.

Reported by a downstream integrator (iPhone 16 Pro Max / iPad 10th generation; HDR10 4K HEVC + EAC3 5.1 bursty ingest through the custom-reader loopback route). Awaiting on-origin retest.

Full macOS suite green (330 tests, 0 failures, 1 skipped), strict-concurrency clean, tvOS Simulator build succeeds.

Full Changelog: 5.15.5...5.16.0