From b3cb95849d3bd5dcf73e70d3d9f89a1f59319634 Mon Sep 17 00:00:00 2001 From: sergystepanov Date: Tue, 21 Apr 2020 20:11:36 +0300 Subject: [PATCH] Refactor input poll (#167) * Refactor input poll to make it x2-3 faster (0.4ms+ -> 0.1ms) * If Server is sendrecv transceiver, client needs to be sendrecv on Firefox. https://github.com/pion/example-webrtc-applications/commit/c641b530a001eb057d8b481185c50bf67d1931b4 Co-authored-by: giongto35 --- web/js/input/input.js | 78 ++++++++++++++++++++++-------------------- web/js/network/rtcp.js | 4 +-- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/web/js/input/input.js b/web/js/input/input.js index e809e3ba..76892d6c 100644 --- a/web/js/input/input.js +++ b/web/js/input/input.js @@ -1,13 +1,9 @@ const input = (() => { - const INPUT_HZ = 100; - const INPUT_STATE_PACKET = 1; - const KEY_BITS = [KEY.A, KEY.B, KEY.X, KEY.Y, KEY.L, KEY.R, KEY.SELECT, KEY.START, KEY.UP, KEY.DOWN, KEY.LEFT, KEY.RIGHT]; + let pollIntervalMs = 10; + let pollIntervalId = 0; + let isStateChanged = false; - let gameInputTimer = null; - let unchangePacket = 0; - - // Game controller state - let keyState = { + let controllerState = { // control [KEY.A]: false, [KEY.B]: false, @@ -15,8 +11,8 @@ const input = (() => { [KEY.Y]: false, [KEY.L]: false, [KEY.R]: false, - [KEY.START]: false, [KEY.SELECT]: false, + [KEY.START]: false, // dpad [KEY.UP]: false, [KEY.DOWN]: false, @@ -24,54 +20,60 @@ const input = (() => { [KEY.RIGHT]: false }; + const keys = Object.keys(controllerState); + const poll = () => { return { + setPollInterval: (ms) => pollIntervalMs = ms, enable: () => { - if (gameInputTimer !== null) return; + if (pollIntervalId > 0) return; - const inputPollInterval = 1000 / INPUT_HZ; - log.info(`[input] setting input polling interval to ${inputPollInterval}ms`); - gameInputTimer = setInterval(sendKeyState, inputPollInterval) + log.info(`[input] poll set to ${pollIntervalMs}ms`); + pollIntervalId = setInterval(sendKeyState, pollIntervalMs) }, disable: () => { - if (gameInputTimer === null) return; + if (pollIntervalId < 1) return; - log.info('[input] stop game input timer'); - clearInterval(gameInputTimer); - gameInputTimer = null; + log.info('[input] poll has been disabled'); + clearInterval(pollIntervalId); + pollIntervalId = 0; } } }; - // relatively slow method const sendKeyState = () => { - // check if state is changed - if (unchangePacket > 0) { - // pack keys state - let bits = ''; - KEY_BITS.slice().reverse().forEach(elem => { - bits += keyState[elem] ? 1 : 0; - }); - let data = parseInt(bits, 2); - - let arrBuf = new Uint8Array(2); - arrBuf[0] = data & ((1 << 8) - 1); - arrBuf[1] = data >> 8; - event.pub(KEY_STATE_UPDATED, arrBuf); - - unchangePacket--; + if (isStateChanged) { + event.pub(KEY_STATE_UPDATED, _encodeState()); + isStateChanged = false; } }; const setKeyState = (name, state) => { - if (name in keyState) { - keyState[name] = state; - unchangePacket = INPUT_STATE_PACKET; + if (controllerState[name] !== undefined) { + controllerState[name] = state; + isStateChanged = true; } }; + /** + * Converts controller state into a binary number. + * + * @returns {Uint8Array} The controller state. + * First byte is controller state. + * Second byte is d-pad state converted (shifted) into a byte. + * So the whole state is just splitted by 8 bits. + * + * @private + */ + const _encodeState = () => { + let result = 0; + for (let i = 0, len = keys.length; i < len; i++) result += controllerState[keys[i]] ? 1 << i : 0; + + return new Uint8Array([result & ((1 << 8) - 1), result >> 8]); + } + return { - poll: poll, - setKeyState: setKeyState + poll, + setKeyState, } })(event, KEY); diff --git a/web/js/network/rtcp.js b/web/js/network/rtcp.js index b23833df..1ffd2e5a 100644 --- a/web/js/network/rtcp.js +++ b/web/js/network/rtcp.js @@ -28,8 +28,8 @@ const rtcp = (() => { }; inputChannel.onclose = () => log.debug('[rtcp] the input channel has closed'); - connection.addTransceiver('video', {'direction': 'recvonly'}); - connection.addTransceiver('audio', {'direction': 'recvonly'}); + connection.addTransceiver('video', {'direction': 'sendrecv'}); + connection.addTransceiver('audio', {'direction': 'sendrecv'}); connection.oniceconnectionstatechange = ice.onIceConnectionStateChange; connection.onicegatheringstatechange = ice.onIceStateChange;