diff options
Diffstat (limited to 'priv/static/room.js')
-rw-r--r-- | priv/static/room.js | 150 |
1 files changed, 0 insertions, 150 deletions
diff --git a/priv/static/room.js b/priv/static/room.js deleted file mode 100644 index 81ecf71..0000000 --- a/priv/static/room.js +++ /dev/null @@ -1,150 +0,0 @@ -"use strict"; - -(() => { - const randomBackoffMilliseconds = (lowest, highest) => { - return Math.round(Math.random() * (highest - lowest) + lowest); - }; - - const prepareInitialInfoMessage = () => { - const dataView = new DataView(new ArrayBuffer(1)); - dataView.setUint8(0, "i".charCodeAt(0)); - return dataView; - }; - - const prepareStateUpdateMessage = (positionMilliseconds, paused) => { - const dataView = new DataView(new ArrayBuffer(10)); - dataView.setUint8(0, "s".charCodeAt(0)); - dataView.setUint8(1, +paused); - dataView.setBigUint64(2, positionMilliseconds); - return dataView; - }; - - const player = videojs("player", { - controls: true, - fill: true, - playsinline: true, - preload: "auto", - }); - player.controlBar.progressControl.disable(); - - const updatePlaybackState = (latestReceivedState, nowMilliseconds) => { - if (nowMilliseconds - latestReceivedState.receivedAtMilliseconds > 2000) { - player.pause(); - return; - } - - const idealPositionMilliseconds = - latestReceivedState.positionMilliseconds + - (nowMilliseconds - latestReceivedState.receivedAtMilliseconds); - const currentPositionMilliseconds = player.currentTime() * 1000; - const positionDiffMilliseconds = currentPositionMilliseconds - idealPositionMilliseconds; - const absPositionDiffMilliseconds = Math.abs(positionDiffMilliseconds); - - if (absPositionDiffMilliseconds > 1250) { - player.currentTime(idealPositionMilliseconds / 1000); - player.playbackRate(1); - } else if ( - absPositionDiffMilliseconds > 200 || - (player.playbackRate() != 1 && absPositionDiffMilliseconds > 100) - ) { - player.playbackRate(1 - 0.02 * Math.sign(positionDiffMilliseconds)); - } else { - player.playbackRate(1); - } - - if (latestReceivedState.paused) { - player.pause(); - player.currentTime(idealPositionMilliseconds / 1000); - } else { - player.play().then(null, () => { - // Failed to play - try muting in case it's because the browser is blocking autoplay - player.muted(true); - player.play().then(null, () => console.error("Failed to play video.")); - }); - } - }; - - const latestReceivedState = { - paused: true, - positionMilliseconds: 0, - receivedAtMilliseconds: null, - }; - - const manageWebsocket = () => { - let websocket = new WebSocket(location.href.replace(/^http/, "ws") + "/websocket"); - websocket.binaryType = "arraybuffer"; - - let initialized = false; - let host; - - // Interval to check video state for non-hosts, and to send state for host - let intervalId; - - websocket.addEventListener("open", (_) => { - console.debug("Created WebSocket connection successfully."); - websocket.send(prepareInitialInfoMessage()); - }); - - websocket.addEventListener("message", (event) => { - const messageDataView = new DataView(event.data); - switch (String.fromCharCode(messageDataView.getUint8(0))) { - case "i": - if (initialized) { - websocket.close(); // Error condition: we're already initialized - } else { - initialized = true; - host = !!messageDataView.getUint8(1); - - // How often host sends state - unused on non-host clients - const SEND_STATE_INTERVAL_MILLISECONDS = 250; - // How often client checks if its state matches what the server sent - unused on hosts - const CHECK_STATE_INTERVAL_MILLISECONDS = 20; - - if (host) { - player.controlBar.progressControl.enable(); - intervalId = setInterval(() => { - websocket.send( - prepareStateUpdateMessage( - BigInt(Math.round(player.currentTime() * 1000)), - player.paused(), - ), - ); - }, SEND_STATE_INTERVAL_MILLISECONDS); - } else { - intervalId = setInterval(() => { - updatePlaybackState(latestReceivedState, performance.now()); - }, CHECK_STATE_INTERVAL_MILLISECONDS); - } - } - break; - case "s": - if (host || !initialized) { - /* Error conditions: host should send state updates, not receive - them, and the server should not send us state updates until - we're initialized. */ - websocket.close(); - } else { - latestReceivedState.paused = messageDataView.getUint8(1); - latestReceivedState.positionMilliseconds = Number(messageDataView.getBigUint64(2)); - latestReceivedState.receivedAtMilliseconds = performance.now(); - } - break; - default: - websocket.close(); // Error condition: unrecognized message type - } - }); - - websocket.addEventListener("close", (_) => { - clearInterval(intervalId); - player.pause(); - const recreateAfter = randomBackoffMilliseconds(50, 3000); - console.debug( - `WebSocket connection closed; will attempt to recreate it in ${recreateAfter} ms.`, - ); - websocket = null; - setTimeout(manageWebsocket, recreateAfter); - }); - }; - - manageWebsocket(); -})(); |