aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/harness
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-21 00:21:50 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-21 00:21:50 +0200
commitf7d33c299ae0d0c4f406779b8f5324d80637adc8 (patch)
tree92102776658fce04451cff90dd616cb4ee26030f /packages/meshbay-hub/tests/harness
parent375ad7d0435a176ad593a32045a5f0182a36d505 (diff)
downloadmeshbay-f7d33c299ae0d0c4f406779b8f5324d80637adc8.tar.gz
perf(hub): bound video read-ahead by bytes, not by seconds
A browser limits bytes, so a bound in seconds had to be sized for the highest-bitrate file and every ordinary one then held a fraction of what the same buffer would have taken. Floored at the old 90 s so nothing pulls less than before, and walked up rather than declared. A refused append now waits for an eviction instead of retrying on every tick. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/tests/harness')
-rw-r--r--packages/meshbay-hub/tests/harness/mse_harness.mjs56
1 files changed, 46 insertions, 10 deletions
diff --git a/packages/meshbay-hub/tests/harness/mse_harness.mjs b/packages/meshbay-hub/tests/harness/mse_harness.mjs
index 91018ef..f0fd04c 100644
--- a/packages/meshbay-hub/tests/harness/mse_harness.mjs
+++ b/packages/meshbay-hub/tests/harness/mse_harness.mjs
@@ -35,7 +35,8 @@ const useCallback = (fn) => fn;
// be written in. Extracting into an order of our own would quietly repair a
// hook declared before its own dependency — a real fault, which reached
// production once, and which the harness is otherwise well placed to catch.
-const src = ['currentRange', 'bufferedAhead', 'evictBehind', 'flushQueue', 'pump']
+const src = ['currentRange', 'bufferedAhead', 'aheadLimit', 'evictBehind',
+ 'flushQueue', 'pump']
.sort((a, b) => app.indexOf(`const ${a} = useCallback(`)
- app.indexOf(`const ${b} = useCallback(`))
.map(grab).join('\n');
@@ -45,14 +46,21 @@ const CAP = capMB * 1048576;
const BITRATE = fileMB * 1048576 / durationS;
// Read from app.js too, so a change to the constants is a change to the test.
+// Arithmetic is allowed because some of them are written as one (`48 * 1024 *
+// 1024`), and refusing anything that is not arithmetic keeps this a reader
+// rather than an evaluator of whatever happens to be on the line.
const constOf = (name) => {
- const m = app.match(new RegExp(`const ${name} = (\\d+)`));
+ const m = app.match(new RegExp(`const ${name} = ([^;]+);`));
if (!m) throw new Error(`${name} not found`);
- return Number(m[1]);
+ if (!/^[\d\s.*/+-]+$/.test(m[1])) throw new Error(`${name} is not a number`);
+ return Number(new Function(`return (${m[1]});`)());
};
const BUFFER_BEHIND_S = constOf('BUFFER_BEHIND_S');
const STREAM_WINDOW = constOf('STREAM_WINDOW');
const BUFFER_AHEAD_S = constOf('BUFFER_AHEAD_S');
+const BUFFER_AHEAD_STEP_BYTES = constOf('BUFFER_AHEAD_STEP_BYTES');
+const BUFFER_AHEAD_MAX_BYTES = constOf('BUFFER_AHEAD_MAX_BYTES');
+const BUFFER_AHEAD_MAX_S = constOf('BUFFER_AHEAD_MAX_S');
const QUEUE_HIGH_WATER = constOf('QUEUE_HIGH_WATER');
const CREDIT_KEEPALIVE_MS = constOf('CREDIT_KEEPALIVE_MS');
@@ -110,7 +118,11 @@ const sb = {
};
let pendingRemoveEvents = 0;
-const video = { currentTime: 0 };
+// `paused` is part of the element the player reads, not decoration: the
+// read-ahead budget only grows while the film is running, and a fake video
+// that never reports being paused would exercise the growing branch in a run
+// whose whole point is that nobody pressed play.
+const video = { currentTime: 0, paused: !playing };
const sbRef = { current: sb }, videoRef = { current: video };
const msRef = { current: { readyState: 'open', endOfStream() {} } };
const queueRef = { current: [] };
@@ -118,6 +130,13 @@ const appendingRef = { current: false }, endedRef = { current: false };
const outstandingRef = { current: 0 }, lastPokeRef = { current: 0 };
const quotaRef = { current: 0 }, stalledRef = { current: false };
const awaitingInitRef = { current: false };
+// The film's own average bitrate, as `stream_init` gives the player, and the
+// budget state the read-ahead walks up from. Both start where the component
+// starts them.
+const bitrateRef = { current: BITRATE };
+const aheadBytesRef = { current: 0 };
+const aheadCapRef = { current: BUFFER_AHEAD_MAX_BYTES };
+const quotaHoldRef = { current: false };
const transportRef = {
current: {
connected: true,
@@ -132,12 +151,16 @@ const fns = new Function(
'sbRef,videoRef,msRef,queueRef,appendingRef,endedRef,outstandingRef,' +
'lastPokeRef,transportRef,BUFFER_BEHIND_S,BUFFER_AHEAD_S,QUEUE_HIGH_WATER,' +
'CREDIT_KEEPALIVE_MS,STREAM_WINDOW,quotaRef,stalledRef,awaitingInitRef,' +
- 'console,useCallback',
- src + '\n return {currentRange, bufferedAhead, evictBehind, flushQueue, pump};'
+ 'bitrateRef,aheadBytesRef,aheadCapRef,quotaHoldRef,' +
+ 'BUFFER_AHEAD_STEP_BYTES,' +
+ 'BUFFER_AHEAD_MAX_BYTES,BUFFER_AHEAD_MAX_S,console,useCallback',
+ src + '\n return {currentRange, bufferedAhead, aheadLimit, evictBehind,'
+ + ' flushQueue, pump};'
)(sbRef, videoRef, msRef, queueRef, appendingRef, endedRef, outstandingRef,
lastPokeRef, transportRef, BUFFER_BEHIND_S, BUFFER_AHEAD_S, QUEUE_HIGH_WATER,
CREDIT_KEEPALIVE_MS, STREAM_WINDOW, quotaRef, stalledRef, awaitingInitRef,
- console, useCallback);
+ bitrateRef, aheadBytesRef, aheadCapRef, quotaHoldRef, BUFFER_AHEAD_STEP_BYTES,
+ BUFFER_AHEAD_MAX_BYTES, BUFFER_AHEAD_MAX_S, console, useCallback);
// The player's own `updateend` listener, transcribed — the one part of the
// component that is a listener rather than a callback, and the place the
@@ -151,6 +174,15 @@ credit = STREAM_WINDOW;
outstandingRef.current = STREAM_WINDOW;
let wall = 0;
const TICK = 0.05;
+// Bytes the link has carried but not yet spent on a whole segment. This used
+// to be re-created inside the loop and thrown away at the end of every tick,
+// so a link slower than one segment per tick — 5 MB/s at this resolution,
+// which is every mobile network there is — delivered *nothing at all* and the
+// run reported a player that had simply never been fed. Anything below about
+// forty megabits was unmeasurable here, which is most of the cases worth
+// measuring. Carried over instead, and capped at one window's worth, because
+// a link cannot bank a burst larger than what the node may have in flight.
+let netBudget = 0;
while (wall < wallS) {
wall += TICK;
if (playing) {
@@ -160,9 +192,9 @@ while (wall < wallS) {
fns.pump(); // the 1 s timer and timeupdate
while (pendingRemoveEvents > 0) { pendingRemoveEvents--; updateend(); }
- let budget = netMBs * 1048576 * TICK;
- while (credit > 0 && budget >= SEG) {
- credit--; budget -= SEG; sent++;
+ netBudget = Math.min(netBudget + netMBs * 1048576 * TICK, SEG * STREAM_WINDOW);
+ while (credit > 0 && netBudget >= SEG) {
+ credit--; netBudget -= SEG; sent++;
outstandingRef.current = Math.max(0, outstandingRef.current - 1);
queueRef.current.push({ byteLength: SEG });
fns.flushQueue();
@@ -176,6 +208,10 @@ console.log(JSON.stringify({
heldInBufferMB: +(bytes / 1048576).toFixed(1),
queueDepth: queueRef.current.length,
bufferedAheadS: +fns.bufferedAhead().toFixed(1),
+ aheadLimitS: +fns.aheadLimit().toFixed(1),
+ budgetMB: +(aheadBytesRef.current / 1048576).toFixed(1),
+ budgetCapMB: +(aheadCapRef.current / 1048576).toFixed(1),
+ bitrateMbits: +(BITRATE * 8 / 1e6).toFixed(2),
watchedS: +video.currentTime.toFixed(1),
grants: granted,
keepalives,