diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-16 20:57:41 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-16 20:57:41 +0200 |
| commit | 373f83236816aaff536dfa2f9589801acac44012 (patch) | |
| tree | acfdb48fa749c28bb19b0b385df7fb6f5c32cfd1 /packages/meshbay-node/tests/test_task_lifetime.py | |
| parent | e4a98ced73541f8d892ed7959cb5c0fefd4dba57 (diff) | |
| download | meshbay-373f83236816aaff536dfa2f9589801acac44012.tar.gz | |
fix(hub): bound the video read-ahead by the playhead, not by the network
A 500 MB film loaded about 100 MB and hung on "buffering" for good. 100 MB is
not a number in our code: it is where the browser stops. ffmpeg remuxes with
`-c copy`, so the bytes on the wire are the file's own, and credit granted per
append meant taking them as fast as the network allowed — which for a film is
very much faster than watching it. The SourceBuffer ceiling arrived in the
first minute.
Past it every append was refused, and the refusal was unrecoverable: a refused
append fires no `updateend`, `updateend` was where credit was granted, so the
node sent nothing and no segment arrived to retry the append. Every wakeup the
pipeline had was downstream of the append that had just failed. Playback
continuing — the one thing that frees room — woke nothing at all.
Credit now follows the buffer instead of the writes. `pump()` is the only
place it is granted, it keeps `STREAM_WINDOW` segments in flight while less
than `BUFFER_AHEAD_S` of film is held past the playhead, and it is driven by a
one-second clock and by playback progress, never by arriving data. Buffering
by time makes a two-hour film cost what a two-minute clip costs.
A window rather than a debt, and this took a second measurement to get right:
accumulating a credit per append and releasing the balance when the buffer
finally drained sent six megabytes in one burst, overshot by a minute of film,
then said nothing for forty-six seconds. Measured in Chrome against real
fragmented MP4.
Two smaller things found on the way. `updateend` fires for `remove()` as well
as `appendBuffer()`, so crediting from it paid the node for the player's own
evictions. And a viewer that is deliberately far enough ahead grants nothing
for minutes, which the node read as a closed tab — it now sends `stream_more`
with n=0, which grants no room but proves someone is there.
The first version of the test modelled the credit loop and passed while the
player still hung: a model written by whoever wrote the fix agrees with it by
construction. `tests/harness/mse_harness.mjs` lifts the real functions out of
app.js as text and runs them against a SourceBuffer that has a ceiling. What is
modelled is the browser.
Diffstat (limited to 'packages/meshbay-node/tests/test_task_lifetime.py')
0 files changed, 0 insertions, 0 deletions