Skip to content

perf(media): start playback sooner and keep Firefox's media worker running - #8

Closed
mickvandijke wants to merge 2 commits into
feat/large-file-streaming-20261015from
perf/media-first-frame
Closed

mickvandijke wants to merge 2 commits into
feat/large-file-streaming-20261015from
perf/media-first-frame

Conversation

@mickvandijke

Copy link
Copy Markdown
Member

Start media playback sooner, and keep Firefox's media service worker running while a source is open. try.autonomi.com streams the same video with the same core, called directly, and started it sooner than createMediaSource(); this brings what it does differently into the SDK.

Stacked on #7, whose 0.1.3 core these measurements use. It targets #7's branch and should be retargeted to main once #7 merges.

  • Plain media reader. createMediaSource() opened its reader in the core's streaming mode. That mode starts fetching up to four records of the next 32 MiB, and the file's last record, alongside the first block the player waits for, so they compete for the same connections. Media sources now open an ordinary reader, which still reads ahead once a read continues a previous one, and keep priming the head and tail.
  • No serial waits. The media service worker installs while the reader opens, and priming starts as soon as the reader is open instead of once the worker controls the page. An open failure still surfaces first; a worker failure surfaces once the reader has opened, which is then closed.
  • Firefox keep-awake. Firefox stops a service worker that has had no new event for 30 s, even one still answering a request, which cut off slow blocks and long responses. While any media source is open the page messages the worker every 10 s, and the worker listens. try shipped the same fix in WithAutonomi/try#17.
  • Worker. It waits up to 120 s for a block instead of 45 s, beyond the core's retries of a missing record after 15 and 45 s. Its responses carry X-Content-Type-Options: nosniff and Content-Security-Policy: sandbox; default-src 'none', since their type comes from the file's metadata.

Trade-off: seeking to the middle after ten seconds of playback is slower (median 6.5 s against 3.4 s), because streaming mode had by then fetched the window holding it. The change favours a fast start. A core change that serves the first requested range before streaming mode's read-ahead would keep both; that belongs in ant-client and is not part of this PR.

Linear issue

Closes V2-1398

Risk tier

  • T0 — docs / tooling / CI / pure UX-output. Repo CI only.
  • T1 — client-only, no network-facing behavior change. CI + prod compat smoke.
  • T2 — node/client logic with behavioral surface, no protocol/format/economics change. Dev testnet + ADR.
  • T3 — protocol / storage format / payments / routing. T2 evidence + adversarial testing.

Proposed for human confirmation: no wire, storage or API change. Media playback now asks the network for the records the player reads, with read-ahead once reads follow each other, rather than streaming mode's window from the start.

Compatibility

  • Wire: none. The same core calls, with { streaming: false } for media readers.
  • Storage: none.
  • API: no type change. createMediaSource() opens a plain reader and installs its worker in parallel with the open; a file of unsupported size (under 3 bytes or beyond Number.MAX_SAFE_INTEGER) can now leave the worker registered before it is refused. The packaged worker gains a message listener, a 120 s block timeout and two response headers. Applications that merged the packaged worker into their own should merge the message listener too, as the README now says.

Semver impact

  • breaking
  • feature
  • fix

Test evidence

Media first-frame audit with the harness, drivers, raw results and logs.

  • Live first frame, SDK media path. AutonomiClient.connect() on mainnet defaults, then createMediaSource() for try's 54.9 MB featured video from its manifest; ten alternating rounds in fresh Chromium 151 contexts, the same harness in both builds. First frame, click to first decoded frame: median 6.6 s against 9.1 s, faster in 7 of 10 pairs. One baseline sample failed (no supported source); every other sample played ten seconds in ten with two waiting events. In every sample the first frame is the read of the first record; later blocks came from cache in about 30 ms. Seek to the middle: median 6.5 s against 3.4 s, as above.
  • try's page, same core. Median first frame 5.0 s with a plain reader against 8.4 s in streaming mode, three rounds each.
  • Firefox 153, real worker, idle limits lowered to 15 s and 1 s, a 22 s block. Baseline: 206 with an empty body after 15 s, the worker stopped mid-answer. This branch: the requested bytes after 22 s, two keep-awake messages while open and none after closing.
  • Playback through the packaged worker in Chromium 151, Firefox 153 and WebKit 26.5: a test-pattern MP4 played past half a second in each, with the new headers present.
  • npm run check: build, TypeScript and 262 tests in 23 files passed. New tests cover the parallel worker setup and priming with a plain reader, a failed worker surfacing after the open and closing the reader, the bridge doing nothing more once its worker controls the page, keep-awake only while a source is open, and the worker's listener, timeout and headers; five of them fail against feat: add large-file streaming, recoverable pointers and manifest exports #7's source, and the ordering test guards the new parallel setup. All five example builds, npm pack --dry-run (97 files), npm run verify:wasm, ADR governance (8 ADRs) and all 52 PR-checker cases passed.

Live timings vary with the network: one machine, Chromium only, one video, unknown node load.

New dependency

none

ADR

n/a for Tier 1. The decision is recorded as a dated amendment to Proposed ADR-0003.

Mitigation / rollback

Revert this PR to restore streaming-mode media readers; deploy the packaged worker with the SDK, as for any SDK release.

🤖 Generated with Claude Code

mickvandijke and others added 2 commits October 22, 2026 17:27
createMediaSource() opened its reader in the core's streaming mode, which
starts fetching ahead, and the file's last record, alongside the first
block a player waits for. On the live network that delayed the first
frame. Media sources now open an ordinary reader, which still reads ahead
once playback's reads follow each other, and keep priming the head and
tail. The media service worker installs while the reader opens, and
priming starts as soon as the reader is open rather than once the worker
controls the page; errors surface in the same order as before.

While any media source is open the page messages the worker every 10 s,
because Firefox stops a worker that has had no new event for 30 s, even
one still answering a request. The worker waits up to 120 s for a block,
beyond the core's retries of a missing record after 15 and 45 s, and
marks its responses nosniff and sandboxed, since their type comes from
the file's metadata.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ten alternating live rounds of createMediaSource() with try's featured
video, a real-Firefox check of the worker keep-awake with lowered idle
limits, playback through the packaged worker in Chromium, Firefox and
WebKit, and the SDK checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mickvandijke

Copy link
Copy Markdown
Member Author

Folded into #7 so it ships in the 0.1.3 release: this PR's two commits are 7b5568a and f177ff4 there, and d06c176 adds the later measurement batches to the audit (the plain reader stayed ahead by less, and no seek difference is claimed). Closing in favour of #7.

🤖 Generated with Claude Code

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