diff options
Diffstat (limited to 'packages/meshbay-hub/tests/harness/mse_harness.mjs')
| -rw-r--r-- | packages/meshbay-hub/tests/harness/mse_harness.mjs | 37 |
1 files changed, 30 insertions, 7 deletions
diff --git a/packages/meshbay-hub/tests/harness/mse_harness.mjs b/packages/meshbay-hub/tests/harness/mse_harness.mjs index 0daacfb..437a542 100644 --- a/packages/meshbay-hub/tests/harness/mse_harness.mjs +++ b/packages/meshbay-hub/tests/harness/mse_harness.mjs @@ -31,7 +31,13 @@ const grab = (name) => { }; const useCallback = (fn) => fn; -const src = ['bufferedAhead', 'evictBehind', 'flushQueue', 'pump'] +// Sorted by where they appear in app.js, not by the order this list happens to +// 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'] + .sort((a, b) => app.indexOf(`const ${a} = useCallback(`) + - app.indexOf(`const ${b} = useCallback(`)) .map(grab).join('\n'); const SEG = 256 * 1024; @@ -68,17 +74,34 @@ const sb = { const e = new Error('quota'); e.name = 'QuotaExceededError'; throw e; } const at = ranges.length ? ranges[ranges.length - 1][1] : 0; - ranges.push([at, at + chunk.byteLength / BITRATE]); + const end = at + chunk.byteLength / BITRATE; + // A real SourceBuffer coalesces contiguous ranges: `buffered` reports the + // spans of media it holds, not the appends that built them. Pushing one + // range per segment made every range a couple of seconds long, which is + // invisible to code that reads `end(length - 1)` and fatal to code that + // looks for the range around the playhead. + if (ranges.length && Math.abs(ranges[ranges.length - 1][1] - at) < 0.001) { + ranges[ranges.length - 1][1] = end; + } else { + ranges.push([at, end]); + } bytes += chunk.byteLength; appended++; }, remove(a, b) { removes++; + // remove(a, b) takes a span out of whatever it overlaps, trimming a range + // rather than only dropping whole ones — otherwise a coalesced range is + // never evicted at all and the buffer grows without limit. let dropped = 0; - ranges = ranges.filter(([s, e]) => { - if (e <= b && s >= a) { dropped += (e - s) * BITRATE; return false; } - return true; - }); + const kept = []; + for (const [s, e] of ranges) { + if (e <= a || s >= b) { kept.push([s, e]); continue; } + if (s < a) kept.push([s, a]); + if (e > b) kept.push([b, e]); + dropped += (Math.min(e, b) - Math.max(s, a)) * BITRATE; + } + ranges = kept; bytes -= dropped; // A real remove() is asynchronous and fires updateend when it lands. That // event is indistinguishable from an append's unless the player kept track. @@ -108,7 +131,7 @@ 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,console,useCallback', - src + '\n return {bufferedAhead, evictBehind, flushQueue, pump};' + src + '\n return {currentRange, bufferedAhead, 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, console, useCallback); |