diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/features/messages.js | 12 | ||||
| -rw-r--r-- | src/hls-player.js | 7 |
2 files changed, 15 insertions, 4 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index 0b6d8f0..13e328a 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -8903,8 +8903,16 @@ export function messagesFeature(ctx) { el.addEventListener('hlsfatal', fail); el.addEventListener('playing', () => { if (w.err) { w.err = null; scheduleRepaint(); } }); const go = () => el.play().catch(() => { el.muted = true; el.play().catch(() => {}); }); - if (!isHls(src) || canHls()) { el.src = src; go(); return; } - import('../hls-player.js').then((m) => m.attachHls(el, src)).then((hls) => { + // A stream that's on plays through hls.js wherever it runs: it keeps the + // viewer near the live edge (starts 1.5 s back, speeds up when it falls + // behind). Chrome's own HLS player starts three segments back, never + // catches up, and fails outright if a page changes its speed. Its player + // still takes recordings, and live streams where hls.js can't run (iOS). + // Measured (go-live-test LATENCY=1, one machine): 2 s segments on + // Chrome's player 8.6 s behind the broadcaster; 1 s segments 4.5 s. + const live = src === liveStream(ev) && isHls(src) && typeof MediaSource !== 'undefined'; + if (!isHls(src) || (canHls() && !live)) { el.src = src; go(); return; } + import('../hls-player.js').then((m) => m.attachHls(el, src, { live })).then((hls) => { if (w.video !== el) { try { hls && hls.destroy(); } catch {} return; } if (!hls) { w.err = 'noplayer'; scheduleRepaint(); return; } w.hls = hls; go(); diff --git a/src/hls-player.js b/src/hls-player.js index d0878e2..18292dd 100644 --- a/src/hls-player.js +++ b/src/hls-player.js @@ -2,10 +2,13 @@ // <video> through Media Source. Chrome and Safari play the playlist as a // plain src, so this file is fetched only when needed. The single-file // build replaces it with a stub (attachHls → null): no stream there. -export async function attachHls(video, src) { +// A stream that's on (`live`) is held near its live edge: started 1.5 s +// back, played up to 1.1× when it drifts past 5 s. +export async function attachHls(video, src, { live = false } = {}) { const { default: Hls } = await import('hls.js'); if (!Hls.isSupported()) return null; - const hls = new Hls({ lowLatencyMode: true, backBufferLength: 30 }); + const hls = new Hls({ lowLatencyMode: true, backBufferLength: 30, + ...(live ? { liveSyncDuration: 1.5, liveMaxLatencyDuration: 5, maxLiveSyncPlaybackRate: 1.1 } : {}) }); hls.loadSource(src); hls.attachMedia(video); hls.on(Hls.Events.ERROR, (_, d) => { |
