Skip to content

Reduce SiloPlayer startup cache contention on remote MKV playback - #1

Draft
blurbery wants to merge 2 commits into
mainfrom
agent/tvos-startup-prefetch-window
Draft

blurbery wants to merge 2 commits into
mainfrom
agent/tvos-startup-prefetch-window

Conversation

@blurbery

Copy link
Copy Markdown
Owner

Summary

Bound SiloPlayer's speculative origin-stream lead to 64 MiB ahead of actual consumer demand until first frame, then immediately restore the normal cache high-water policy.

This targets avoidable tvOS startup work on large remote MKV files while preserving the existing client-side Dolby Vision Profile 7 → Profile 8.1 transform and steady-state buffering behavior.

Important

This PR is a review draft in blurbery/silo-apple. It is not deployed to an Apple TV, not submitted to Silo-Server/silo-apple, and does not change the installed TestFlight/App Store build.

Production evidence

The measured playback was an 81.3 GB, 4K HEVC, Dolby Vision Profile 7, TrueHD 7.1 MKV using Protocol V3's client_dv7_to_dv81 transformation.

Stage Observed result
/api/v1/playback/start 266 ms
Plan selected → first frame 7,984 ms
Origin range from byte 0 244,478,242 bytes over 4,061 ms before cancellation
Subsequent re-anchored origin range 277,288,568 bytes over 5,728 ms before cancellation
Additional planning probes MKV tail, midpoint, and early-file range chunks

The Apple source cache allows a 256 MiB loopback window on non-constrained devices. During startup, startPrefetch creates the sequential origin stream before the segment writer finishes its latency-sensitive Matroska index work. The window can therefore approach its full cache budget while head/tail/midpoint chunk probes occupy the same cache and network path.

Note

The eviction/re-anchor explanation is an evidence-backed inference: the byte-zero stream stopped near the source-cache high-water mark, probe chunks arrived concurrently, and the next long-lived request restarted around byte 32.6 MB. Device-side [CMP-SOURCE-CACHE] logs are still required to prove the exact cache eviction sequence.

Root cause

The origin window's existing pause policy considers the global cache budget and blocked demand, but has no startup-specific bound on how far speculative transfer may lead real consumption. That is desirable after playback is established, when a large forward cache provides outage protection. It is counterproductive while the VOD planner is still probing metadata and has not produced the first local HLS fragment.

The result can be:

  1. unnecessary pre-first-frame transfer;
  2. bandwidth competition with metadata probes;
  3. early cache pressure from unrelated source regions;
  4. eviction of startup bytes followed by a window re-anchor;
  5. a longer time to the first locally generated fragment and first rendered frame.

Implementation

  • Add an optional maximumAheadBytes input to the pure origin-stream pause policy.
  • Keep blocked demand authoritative: a demand at or ahead of the write cursor never parks, even when the startup limit or global cache budget is exhausted.
  • Apply a 64 MiB ahead-of-demand limit only to .siloPlayerLoopback source proxies.
  • Advance the demand mark through the existing cached-read hints, so the window continues moving with the real demuxer instead of stopping after 64 MiB total.
  • Release the startup limit on the first observed frame and nudge the existing warm stream without reconnecting it.
  • Preserve the normal 128/256 MiB source-cache budgets, disk spill, outage runway, and steady-state behavior after first frame.
  • Add focused tests for speculative parking and blocked-demand override.

Warning

This is not a 64 MiB download cap. It is a moving lead window. If the demuxer consumes byte N, the origin may continue up to roughly N + 64 MiB; a blocked read always resumes the stream immediately.

Caution

Do not merge upstream or distribute a build until the compile and validation checklist below is complete. The configured remote Xcode builder was unreachable from the current environment, and the local Mac has Command Line Tools only with a mismatched Swift SDK/toolchain.

Validation completed

  • git diff --check passed.
  • Swift frontend syntax parsing passed for every changed source and test file.
  • The new pure-policy cases cover:
    • parking at the 64 MiB speculative lead with global budget still available;
    • continuing below the lead limit;
    • blocked demand overriding both the startup limit and an exhausted global budget.

Validation still required

  • Generate Silo.xcodeproj with XcodeGen.
  • Compile the SiloTV scheme against a tvOS 26 SDK.
  • Run PlaybackOriginStreamPolicyTests on a tvOS simulator.
  • Test on a physical Apple TV with the same high-bitrate DV7/TrueHD MKV.
  • Capture [CMP-TTFF] and [CMP-SOURCE-CACHE] logs for at least five cold starts before and after.
  • Confirm no new startup stall, rebuffer, origin detach/reconnect, or seek regression.
  • Confirm first-frame release restores the expected steady-state cache ahead and origin bitrate.

Acceptance criteria

Tip

Treat this as successful only if median and p95 TTFF improve without increasing the post-start rebuffer rate. One fast start is not sufficient evidence.

Suggested comparison fields:

Metric Baseline Candidate
Median first-frame time Capture 5+ cold starts Must improve materially
p95 first-frame time Capture 5+ cold starts Must not regress
Bytes transferred before first frame ~244 MB byte-zero stream observed Expected to fall toward bounded lead + probes
Window re-anchors before first frame Capture device logs Expected to fall or remain zero
Rebuffers in first 10 minutes Capture device logs Must not increase

Risk and rollback

  • Risk: a source whose producer consumes unusually bursty data may spend more time close to the moving 64 MiB boundary before first frame.
  • Mitigation: demand at the cursor overrides the bound, the stream stays connected while parked, and the cap disappears after first frame.
  • Scope: only SiloPlayer loopback receives the startup limit; NativePlayer HLS/direct and CompatibilityPlayer behavior are unchanged.
  • Rollback: revert this single commit. There are no settings migrations, API changes, or persisted cache-format changes.

Privacy and security

  • No server address, authentication token, profile identifier, personal name, or media title is included in the change.
  • Logging adds only a static message when the startup limit is released.

Keep the loopback source window at most 64 MiB ahead of real consumer demand until first frame, then restore the normal full cache high-water policy. This prevents head/tail/midpoint MKV probes from competing with a cache-filling speculative stream and evicting startup bytes. Blocked demand always overrides the limit, so this is not a download quota and cannot strand the reader. Adds focused policy coverage. Syntax parsing passes; full Xcode/tvOS compilation and physical Apple TV validation remain required because no reachable Xcode builder is available in this environment.
@blurbery

Copy link
Copy Markdown
Owner Author

Remote HLS first-frame follow-up — 4fad550

The server timing trace showed that Silo delivered the HLS master playlist in about 1.18 seconds and the first complete playable media fragment in about 2.17 seconds, while the Apple client displayed video after roughly 5–6 seconds. That isolates approximately three seconds of residual startup behavior inside AVPlayer initialization and conservative buffering.

Two-phase AVPlayer policy

For .remoteHLS only, the backend now:

  1. creates the item with a one-second preferredForwardBufferDuration;
  2. temporarily disables automaticallyWaitsToMinimizeStalling;
  3. requests playImmediately(atRate: 1.0) when the initial display gate is armed; and
  4. after the first displayed-frame gate releases, resets the explicit buffer target to the system default and re-enables automatic waiting.

Direct-file playback keeps AVPlayer's defaults. The existing local SiloPlayer loopback policy continues to use its independent four-second startup target and bitrate-aware steady-state buffer.

Important

The aggressive policy exists only between item creation and the first displayed frame. Normal AVPlayer anti-stall behavior is restored for the rest of playback.

Note

Apple's preferredForwardBufferDuration is only a preference, not a guaranteed byte or time threshold. playImmediately(atRate:) is therefore the explicit instruction that available media should begin without waiting for AVPlayer's conservative stall-minimization heuristic.

Warning

A faster first frame intentionally trades some startup cushion for latency. Restoring automatic waiting immediately after first frame bounds that exposure, but physical-device cold-start and rebuffer measurements are still required before proposing this upstream.

Regression coverage and validation

  • A pure policy test proves remote HLS selects the one-second fast-start policy.
  • A second test proves remote direct playback retains system buffering.
  • git diff --check passed.
  • Swift frontend parsing passed for the changed backend and test file.
  • A full tvOS 26 compile/test remains required because the current Mac has Command Line Tools rather than full Xcode.

Expected result

The server-side one-second fragment change can remove roughly one GOP. This client change targets the larger residual delay after playable media is already available. Together they should move startup materially closer to the measured server-media-ready point, although the exact gain must be measured on the Apple TV rather than inferred from API timing alone.

Caution

This source commit is not active in the installed Apple app until a signed tvOS build containing this branch is installed. The server-side half can be deployed and measured independently.

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