- element = document.getElementById("playerUI");
- }
- if (!element) {
- return;
- }
- if (element.requestFullscreen) {
- element.requestFullscreen();
- } else if (element.mozRequestFullScreen) {
- element.mozRequestFullScreen();
- } else if (element.webkitRequestFullscreen) {
- element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
- } else if (element.msRequestFullscreen) {
- element.msRequestFullscreen();
- } else if (element.webkitEnterFullscreen) {
- element.webkitEnterFullscreen(); //for iphone this code worked
- }
- }
- onFullscreenChange()
-}
-
-function onFullscreenChange() {
- isFullscreen = (document.webkitIsFullScreen
- || document.mozFullScreen
- || (document.msFullscreenElement && document.msFullscreenElement !== null)
- || (document.fullscreenElement && document.fullscreenElement !== null));
-
- let minimize = document.getElementById('minimize');
- let maximize = document.getElementById('maximize');
- if (minimize && maximize) {
- if (isFullscreen) {
- minimize.style.display = 'inline';
- maximize.style.display = 'none';
- } else {
- minimize.style.display = 'none';
- maximize.style.display = 'inline';
- }
- }
-}
-
-function parseURLParams() {
- let urlParams = new URLSearchParams(window.location.search);
- inputOptions.controlScheme = (urlParams.has('hoveringMouse') ? ControlSchemeType.HoveringMouse : ControlSchemeType.HoveringMouse);
- let schemeToggle = document.getElementById("control-scheme-text");
- switch (inputOptions.controlScheme) {
- case ControlSchemeType.HoveringMouse:
- schemeToggle.innerHTML = "Control Scheme: Hovering Mouse";
- break;
- case ControlSchemeType.LockedMouse:
- schemeToggle.innerHTML = "Control Scheme: Locked Mouse";
- break;
- default:
- schemeToggle.innerHTML = "Control Scheme: Locked Mouse";
- console.log(`ERROR: Unknown control scheme ${inputOptions.controlScheme}, defaulting to Locked Mouse`);
- break;
- }
-
- if (urlParams.has('noWatermark')) {
- let watermark = document.getElementById("unrealengine");
- watermark.style.display = 'none';
- }
-
- inputOptions.hideBrowserCursor = (urlParams.has('hideBrowserCursor') ? true : false);
-}
-
-
-function setupHtmlEvents() {
- //Window events
- window.addEventListener('resize', resizePlayerStyle, true);
- window.addEventListener('orientationchange', onOrientationChange);
-
- //Gamepad events
- if (haveEvents) {
- window.addEventListener("gamepadconnected", gamepadConnectHandler);
- window.addEventListener("gamepaddisconnected", gamepadDisconnectHandler);
- } else if (haveWebkitEvents) {
- window.addEventListener("webkitgamepadconnected", gamepadConnectHandler);
- window.addEventListener("webkitgamepaddisconnected", gamepadDisconnectHandler);
- }
-
- document.addEventListener('webkitfullscreenchange', onFullscreenChange, false);
- document.addEventListener('mozfullscreenchange', onFullscreenChange, false);
- document.addEventListener('fullscreenchange', onFullscreenChange, false);
- document.addEventListener('MSFullscreenChange', onFullscreenChange, false);
-
- let settingsBtn = document.getElementById('settingsBtn');
- settingsBtn.addEventListener('click', settingsClicked);
-
- let statsBtn = document.getElementById('statsBtn');
- statsBtn.addEventListener('click', statsClicked);
-
- let controlBtn = document.getElementById('control-tgl');
- controlBtn.addEventListener('change', toggleControlScheme);
-
- let cursorBtn = document.getElementById('cursor-tgl');
- cursorBtn.addEventListener('change', toggleBrowserCursorVisibility);
-
- let resizeCheckBox = document.getElementById('enlarge-display-to-fill-window-tgl');
- if (resizeCheckBox !== null) {
- resizeCheckBox.onchange = function (event) {
- resizePlayerStyle();
- };
- }
-
- qualityControlOwnershipCheckBox = document.getElementById('quality-control-ownership-tgl');
- if (qualityControlOwnershipCheckBox !== null) {
- qualityControlOwnershipCheckBox.onchange = function (event) {
- requestQualityControl();
- };
- }
-
- let encoderParamsSubmit = document.getElementById('encoder-params-submit');
- if (encoderParamsSubmit !== null) {
- encoderParamsSubmit.onclick = function (event) {
-
- let minQP = document.getElementById('encoder-min-qp-text').value;
- let maxQP = document.getElementById('encoder-max-qp-text').value;
-
- emitCommand({ "Encoder.MinQP": minQP });
- emitCommand({ "Encoder.MaxQP": maxQP });
- };
- }
-
- let webrtcParamsSubmit = document.getElementById('webrtc-params-submit');
- if (webrtcParamsSubmit !== null) {
- webrtcParamsSubmit.onclick = function (event) {
- let FPS = document.getElementById('webrtc-fps-text').value;
- let minBitrate = document.getElementById('webrtc-min-bitrate-text').value * 1000;
- let maxBitrate = document.getElementById('webrtc-max-bitrate-text').value * 1000;
-
- emitCommand({ 'WebRTC.Fps': FPS });
- emitCommand({ 'WebRTC.MinBitrate': minBitrate });
- emitCommand({ 'WebRTC.MaxBitrate': maxBitrate });
- };
- }
-
- let showFPSButton = document.getElementById('show-fps-button');
- if (showFPSButton !== null) {
- showFPSButton.onclick = function (event) {
- emitCommand({ "Stat.FPS": '' });
- };
- }
-
- let requestKeyframeButton = document.getElementById('request-keyframe-button');
- if (requestKeyframeButton !== null) {
- requestKeyframeButton.onclick = function (event) {
- toStreamerHandlers.IFrameRequest("IFrameRequest");
- };
- }
-
- let restartStreamButton = document.getElementById('restart-stream-button');
- if (restartStreamButton !== null) {
- restartStreamButton.onmousedown = function (event) {
- restartStream();
- };
- }
-
- let matchViewportResolutionCheckBox = document.getElementById('match-viewport-res-tgl');
- if (matchViewportResolutionCheckBox !== null) {
- matchViewportResolutionCheckBox.onchange = function (event) {
- matchViewportResolution = matchViewportResolutionCheckBox.checked;
- updateVideoStreamSize();
- };
- }
-
- let statsCheckBox = document.getElementById('show-stats-tgl');
- if (statsCheckBox !== null) {
- statsCheckBox.onchange = function (event) {
- let stats = document.getElementById('statsContainer');
- stats.style.display = event.target.checked ? "block" : "none";
- };
- }
-
- let latencyButton = document.getElementById('test-latency-button');
- if (latencyButton) {
- latencyButton.onclick = () => {
- sendStartLatencyTest();
- };
- }
-
- // Setup toggle and pair with some URL query string param.
- setupToggleWithUrlParams("prefer-sfu-tgl", "preferSFU");
- setupToggleWithUrlParams("use-mic-tgl", "useMic");
- setupToggleWithUrlParams("force-turn-tgl", "ForceTURN");
- setupToggleWithUrlParams("force-mono-tgl", "ForceMonoAudio");
- setupToggleWithUrlParams("control-tgl", "hoveringMouse");
- setupToggleWithUrlParams("control-tgl", "hoveringMouse");
- setupToggleWithUrlParams("offer-receive-tgl", "offerToReceive");
- setupToggleWithUrlParams("cursor-tgl", "hideBrowserCursor");
-
-
-
-
- var streamSelector = document.getElementById('stream-select');
- var trackSelector = document.getElementById('track-select');
- if (streamSelector) {
- streamSelector.onchange = function (event) {
- const stream = webRtcPlayerObj.availableVideoStreams.get(streamSelector.value);
- webRtcPlayerObj.video.srcObject = stream;
- streamTrackSource = stream;
- webRtcPlayerObj.video.play();
- updateTrackList();
- }
-
- if (trackSelector) {
- trackSelector.onchange = function (event) {
- if (!streamTrackSource) {
- streamTrackSource = webRtcPlayerObj.availableVideoStreams.get(streamSelector.value);
- }
- if (streamTrackSource) {
- for (const track of streamTrackSource.getVideoTracks()) {
- if (track.id == trackSelector.value) {
- webRtcPlayerObj.video.srcObject = new MediaStream([track]);
- webRtcPlayerObj.video.play();
- streamSelector.value = "";
- break;
- }
- }
- }
- }
- }
- }
-}
-
-function setupToggleWithUrlParams(toggleId, urlParameterKey) {
- let toggleElem = document.getElementById(toggleId);
- if (toggleElem) {
- toggleElem.checked = new URLSearchParams(window.location.search).has(urlParameterKey);
- toggleElem.addEventListener('change', (event) => {
- const urlParams = new URLSearchParams(window.location.search);
- if (event.currentTarget.checked) {
- urlParams.set(urlParameterKey, "true");
- } else {
- urlParams.delete(urlParameterKey);
- }
- window.history.replaceState({}, '', urlParams.toString() !== "" ? `${window.location.pathname}?${urlParams}` : `${window.location.pathname}`);
- });
- }
-}
-
-function UrlParamsCheck(urlParameterKey) {
- return new URLSearchParams(window.location.search).has(urlParameterKey);
-}
-
-var streamTrackSource = null;
-
-function updateStreamList() {
- const streamSelector = document.getElementById('stream-select');
- for (let i = streamSelector.options.length - 1; i >= 0; i--) {
- streamSelector.remove(i);
- }
- streamSelector.value = null;
- for (const [streamId, stream] of webRtcPlayerObj.availableVideoStreams) {
- var opt = document.createElement('option');
- opt.value = streamId;
- opt.innerHTML = streamId;
- streamSelector.appendChild(opt);
- if (streamSelector.value == null) {
- streamSelector.value = streamId;
- }
- }
-
- updateTrackList();
-}
-
-function updateTrackList() {
- const streamSelector = document.getElementById('stream-select');
- const trackSelector = document.getElementById('track-select');
- const stream = webRtcPlayerObj.availableVideoStreams.get(streamSelector.value);
- for (let i = trackSelector.options.length - 1; i >= 0; i--) {
- trackSelector.remove(i);
- }
- trackSelector.value = null;
- for (const track of stream.getVideoTracks()) {
- var opt = document.createElement('option');
- opt.value = track.id;
- opt.innerHTML = track.label;
- trackSelector.appendChild(opt);
- if (track.selected) {
- trackSelector.value = track.id;
- }
- }
-}
-
-function sendStartLatencyTest() {
- // We need WebRTC to be active to do a latency test.
- if (!webRtcPlayerObj) {
- return;
- }
-
- let onTestStarted = function (StartTimeMs) {
- let descriptor = {
- StartTime: StartTimeMs
- };
- emitDescriptor("LatencyTest", descriptor);
- };
-
- webRtcPlayerObj.startLatencyTest(onTestStarted);
-}
-
-function setOverlay(htmlClass, htmlElement, onClickFunction) {
- let videoPlayOverlay = document.getElementById('videoPlayOverlay');
- if (!videoPlayOverlay) {
- let playerDiv = document.getElementById('player');
- videoPlayOverlay = document.createElement('div');
- videoPlayOverlay.id = 'videoPlayOverlay';
- playerDiv.appendChild(videoPlayOverlay);
- }
-
- // Remove existing html child elements so we can add the new one
- while (videoPlayOverlay.lastChild) {
- videoPlayOverlay.removeChild(videoPlayOverlay.lastChild);
- }
-
- if (htmlElement)
- videoPlayOverlay.appendChild(htmlElement);
-
- if (onClickFunction) {
- videoPlayOverlay.addEventListener('click', function onOverlayClick(event) {
- onClickFunction(event);
- videoPlayOverlay.removeEventListener('click', onOverlayClick);
- });
- }
-
- // Remove existing html classes so we can set the new one
- let cl = videoPlayOverlay.classList;
- for (let i = cl.length - 1; i >= 0; i--) {
- cl.remove(cl[i]);
- }
-
- videoPlayOverlay.classList.add(htmlClass);
-}
-
-function showConnectOverlay() {
- let startText = document.createElement('div');
- startText.id = 'container'
- let title = document.createElement('h2')
- title.id = "title"
- title.innerHTML = 'Демонстрация начата'
- let caption = document.createElement('p')
- caption.id = 'caption'
- caption.innerHTML = 'Нажмите, чтобы продолжить'
- let headerContainer = document.createElement('div')
- headerContainer.appendChild(title)
- headerContainer.appendChild(caption)
- startText.appendChild(headerContainer)
- let playButton = document.createElement('img')
- playButton.id = 'playButton'
- playButton.src = play
- startText.appendChild(playButton)
- let buttonBack = document.createElement('a')
- let captionBack = document.createElement('div')
- captionBack.innerHTML = 'Выбор жилого комплекса'
- buttonBack.appendChild(captionBack)
- buttonBack.id = 'link'
- buttonBack.href = 'https://stream.graff.tech/'
- startText.appendChild(buttonBack)
-
- setOverlay('clickableState', startText, event => {
- connect();
- startAfkWarningTimer();
- });
-}
-
-function showLoader() {
- let loader = document.createElement('span')
- loader.id = 'loader'
- setOverlay('textDisplayState', loader)
-
-}
-
-function showTextOverlay(text) {
- let textOverlay = document.createElement('div');
- textOverlay.id = 'messageOverlay';
- textOverlay.innerHTML = text ? text : '';
- setOverlay('textDisplayState', textOverlay);
-}
-
-function playStream() {
- if (webRtcPlayerObj && webRtcPlayerObj.video) {
- if (webRtcPlayerObj.audio.srcObject && autoPlayAudio) {
- // Video and Audio are seperate tracks
- webRtcPlayerObj.audio.play().then(() => {
- // audio play has succeeded, start playing video
- playVideo();
- }).catch((onRejectedReason) => {
- console.error(onRejectedReason);
- console.log("Browser does not support autoplaying audio without interaction - to resolve this we are going to show the play button overlay.")
- showPlayOverlay();
- });
- } else {
- // Video and audio are combined in the video element
- playVideo();
- }
- showFreezeFrameOverlay();
- hideOverlay();
- }
-}
-
-function playVideo() {
- webRtcPlayerObj.video.play().catch((onRejectedReason) => {
- if (webRtcPlayerObj.audio.srcObject) {
- webRtcPlayerObj.audio.stop();
- }
- console.error(onRejectedReason);
- console.log("Browser does not support autoplaying video without interaction - to resolve this we are going to show the play button overlay.")
- showPlayOverlay();
- });
-}
-
-function showPlayOverlay() {
- let img = document.createElement('img');
- img.id = 'playButton';
- img.src = play
- img.alt = 'Start Streaming';
- setOverlay('clickableState', img, event => {
- playStream();
- });
- shouldShowPlayOverlay = false;
-}
-
-function updateAfkOverlayText() {
- afk.overlay.innerHTML = '
No activity detected
Disconnecting in ' + afk.countdown + ' seconds
Click to continue
';
-}
-
-function showAfkOverlay() {
- // Pause the timer while the user is looking at the inactivity warning overlay.
- stopAfkWarningTimer();
-
- // Show the inactivity warning overlay.
- afk.overlay = document.createElement('div');
- afk.overlay.id = 'afkOverlay';
- setOverlay('clickableState', afk.overlay, event => {
- // The user clicked so start the timer again and carry on.
- hideOverlay();
- clearInterval(afk.countdownTimer);
- startAfkWarningTimer();
- });
-
- afk.countdown = afk.closeTimeout;
- updateAfkOverlayText();
-
- if (inputOptions.controlScheme == ControlSchemeType.LockedMouse && document.exitPointerLock) {
- document.exitPointerLock();
- }
-
- afk.countdownTimer = setInterval(function () {
- afk.countdown--;
- if (afk.countdown == 0) {
- // The user failed to click so disconnect them.
- hideOverlay();
- ws.close();
- } else {
- // Update the countdown message.
- updateAfkOverlayText();
- }
- }, 1000);
-}
-
-function hideOverlay() {
- setOverlay('hiddenState');
-}
-
-// Start a timer which when elapsed will warn the user they are inactive.
-function startAfkWarningTimer() {
- afk.active = afk.enabled;
- resetAfkWarningTimer();
-}
-
-// Stop the timer which when elapsed will warn the user they are inactive.
-function stopAfkWarningTimer() {
- afk.active = false;
-}
-
-// If the user interacts then reset the warning timer.
-function resetAfkWarningTimer() {
- if (afk.active) {
- clearTimeout(afk.warnTimer);
- afk.warnTimer = setTimeout(function () {
- showAfkOverlay();
- }, afk.warnTimeout * 1000);
- }
-}
-
-function createWebRtcOffer() {
- console.log('loaderLOADER')
- if (webRtcPlayerObj) {
- console.log('Creating offer');
- showLoader();
- webRtcPlayerObj.createOffer();
- } else {
- console.log('WebRTC player not setup, cannot create offer');
- showTextOverlay('Unable to setup video');
- }
-}
-
-function sendInputData(data) {
- if (webRtcPlayerObj) {
- resetAfkWarningTimer();
- webRtcPlayerObj.send(data);
- }
-}
-
-function addResponseEventListener(name, listener) {
- responseEventListeners.set(name, listener);
-}
-
-function removeResponseEventListener(name) {
- responseEventListeners.delete(name);
-}
-
-function showFreezeFrame() {
- let base64 = btoa(freezeFrame.jpeg.reduce((data, byte) => data + String.fromCharCode(byte), ''));
- let freezeFrameImage = document.getElementById("freezeFrameOverlay").childNodes[0];
- freezeFrameImage.src = 'data:image/jpeg;base64,' + base64;
- freezeFrameImage.onload = function () {
- freezeFrame.height = freezeFrameImage.naturalHeight;
- freezeFrame.width = freezeFrameImage.naturalWidth;
- resizeFreezeFrameOverlay();
- if (shouldShowPlayOverlay) {
- resizePlayerStyle();
- } else {
- showFreezeFrameOverlay();
- }
- setTimeout(() => {
- webRtcPlayerObj.setVideoEnabled(false);
- }, freezeFrameDelay);
- };
-}
-
-function processFileExtension(view) {
- // Reset file if we got a file message and we are not "receiving" it yet
- if (!file.receiving) {
- file.mimetype = "";
- file.extension = "";
- file.receiving = true;
- file.valid = false;
- file.size = 0;
- file.data = [];
- file.timestampStart = (new Date()).getTime();
- console.log('Received first chunk of file');
- }
-
- let extensionAsString = new TextDecoder("utf-16").decode(view.slice(1));
- console.log(extensionAsString);
- file.extension = extensionAsString;
-}
-
-function processFileMimeType(view) {
- // Reset file if we got a file message and we are not "receiving" it yet
- if (!file.receiving) {
- file.mimetype = "";
- file.extension = "";
- file.receiving = true;
- file.valid = false;
- file.size = 0;
- file.data = [];
- file.timestampStart = (new Date()).getTime();
- console.log('Received first chunk of file');
- }
-
- let mimeAsString = new TextDecoder("utf-16").decode(view.slice(1));
- console.log(mimeAsString);
- file.mimetype = mimeAsString;
-}
-
-
-function processFileContents(view) {
- // If we haven't received the intial setup instructions, return
- if (!file.receiving) return;
-
- // Extract the toal size of the file (across all chunks)
- file.size = Math.ceil((new DataView(view.slice(1, 5).buffer)).getInt32(0, true) / 16379 /* The maximum number of payload bits per message*/);
-
- // Get the file part of the payload
- let fileBytes = view.slice(1 + 4);
-
- // Append to existing data that holds the file
- file.data.push(fileBytes);
-
- // Uncomment for debug
- console.log(`Received file chunk: ${file.data.length}/${file.size}`);
-
- if (file.data.length === file.size) {
- file.receiving = false;
- file.valid = true;
- console.log("Received complete file");
- const transferDuration = ((new Date()).getTime() - file.timestampStart);
- const transferBitrate = Math.round(file.size * 16 * 1024 / transferDuration);
- console.log(`Average transfer bitrate: ${transferBitrate}kb/s over ${transferDuration / 1000} seconds`);
-
- // File reconstruction
- /**
- * Example code to reconstruct the file
- *
- * This code reconstructs the received data into the original file based on the mime type and extension provided and then downloads the reconstructed file
- */
- var received = new Blob(file.data, { type: file.mimetype });
- var a = document.createElement('a');
- a.setAttribute('href', URL.createObjectURL(received));
- a.setAttribute('download', `transfer.${file.extension}`);
- document.body.append(a);
- // if you are so inclined to make it auto-download, do something like: a.click();
- a.remove();
- }
- else if (file.data.length > file.size) {
- file.receiving = false;
- console.error(`Received bigger file than advertised: ${file.data.length}/${file.size}`);
- }
-}
-
-function processFreezeFrameMessage(view) {
- // Reset freeze frame if we got a freeze frame message and we are not "receiving" yet.
- if (!freezeFrame.receiving) {
- freezeFrame.receiving = true;
- freezeFrame.valid = false;
- freezeFrame.size = 0;
- freezeFrame.jpeg = undefined;
- }
-
- // Extract total size of freeze frame (across all chunks)
- freezeFrame.size = (new DataView(view.slice(1, 5).buffer)).getInt32(0, true);
-
- // Get the jpeg part of the payload
- let jpegBytes = view.slice(1 + 4);
-
- // Append to existing jpeg that holds the freeze frame
- if (freezeFrame.jpeg) {
- let jpeg = new Uint8Array(freezeFrame.jpeg.length + jpegBytes.length);
- jpeg.set(freezeFrame.jpeg, 0);
- jpeg.set(jpegBytes, freezeFrame.jpeg.length);
- freezeFrame.jpeg = jpeg;
- }
- // No existing freeze frame jpeg, make one
- else {
- freezeFrame.jpeg = jpegBytes;
- freezeFrame.receiving = true;
- console.log(`received first chunk of freeze frame: ${freezeFrame.jpeg.length}/${freezeFrame.size}`);
- }
-
- // Uncomment for debug
- //console.log(`Received freeze frame chunk: ${freezeFrame.jpeg.length}/${freezeFrame.size}`);
-
- // Finished receiving freeze frame, we can show it now
- if (freezeFrame.jpeg.length === freezeFrame.size) {
- freezeFrame.receiving = false;
- freezeFrame.valid = true;
- console.log(`received complete freeze frame ${freezeFrame.size}`);
- showFreezeFrame();
- }
- // We received more data than the freeze frame payload message indicate (this is an error)
- else if (freezeFrame.jpeg.length > freezeFrame.size) {
- console.error(`received bigger freeze frame than advertised: ${freezeFrame.jpeg.length}/${freezeFrame.size}`);
- freezeFrame.jpeg = undefined;
- freezeFrame.receiving = false;
- }
-}
-
-function setupWebRtcPlayer(htmlElement, config) {
- webRtcPlayerObj = new webRtcPlayer(config);
- autoPlayAudio = typeof config.autoPlayAudio !== 'undefined' ? config.autoPlayAudio : true;
- htmlElement.appendChild(webRtcPlayerObj.video);
- htmlElement.appendChild(webRtcPlayerObj.audio);
- htmlElement.appendChild(freezeFrameOverlay);
-
- webRtcPlayerObj.onWebRtcOffer = function (offer) {
- if (ws && ws.readyState === WS_OPEN_STATE) {
- let offerStr = JSON.stringify(offer);
- console.log("%c[Outbound SS message (offer)]", "background: lightgreen; color: black", offer);
- ws.send(offerStr);
- }
- };
-
- webRtcPlayerObj.onWebRtcCandidate = function (candidate) {
- if (ws && ws.readyState === WS_OPEN_STATE) {
- ws.send(JSON.stringify({
- type: 'iceCandidate',
- candidate: candidate
- }));
- }
- };
-
- webRtcPlayerObj.onWebRtcAnswer = function (answer) {
- if (ws && ws.readyState === WS_OPEN_STATE) {
- let answerStr = JSON.stringify(answer);
- console.log("%c[Outbound SS message (answer)]", "background: lightgreen; color: black", answer);
- ws.send(answerStr);
-
- if (webRtcPlayerObj.sfu) {
- // Send data channel setup request to the SFU
- const requestMsg = { type: "dataChannelRequest" };
- console.log("%c[Outbound SS message (dataChannelRequest)]", "background: lightgreen; color: black", requestMsg);
- ws.send(JSON.stringify(requestMsg));
- }
- }
- };
-
- webRtcPlayerObj.onSFURecvDataChannelReady = function () {
- if (webRtcPlayerObj.sfu) {
- // Send SFU a message to let it know browser data channels are ready
- const requestMsg = { type: "peerDataChannelsReady" };
- console.log("%c[Outbound SS message (peerDataChannelsReady)]", "background: lightgreen; color: black", requestMsg);
- ws.send(JSON.stringify(requestMsg));
- }
- }
-
- webRtcPlayerObj.onVideoInitialised = function () {
- if (ws && ws.readyState === WS_OPEN_STATE) {
- resizePlayerStyle();
- playStream();
- }
- };
-
- webRtcPlayerObj.onNewVideoTrack = function (streams) {
- if (webRtcPlayerObj.video && webRtcPlayerObj.video.srcObject && webRtcPlayerObj.onVideoInitialised) {
- webRtcPlayerObj.onVideoInitialised();
- }
- updateStreamList();
- }
-
- webRtcPlayerObj.onDataChannelMessage = function (data) {
- let view = new Uint8Array(data);
- try {
- let messageType = fromStreamerMessages.getFromValue(view[0]);
- fromStreamerHandlers[messageType](data);
- } catch (e) {
- console.error(`Custom data channel message with message type that is unknown to the Pixel Streaming protocol. Does your PixelStreamingProtocol need updating? The message type was: ${view[0]}`);
- }
- };
-
- registerInputs(webRtcPlayerObj.video);
-
- // On a touch device we will need special ways to show the on-screen keyboard.
- if ('ontouchstart' in document.documentElement) {
- createOnScreenKeyboardHelpers(htmlElement);
- }
-
- if (true) {
- createWebRtcOffer();
- }
-
- return webRtcPlayerObj.video;
-}
-
-function setupStats() {
- webRtcPlayerObj.aggregateStats(1 * 1000 /*Check every 1 second*/);
-
- let printInterval = 5 * 60 * 1000; /*Print every 5 minutes*/
- let nextPrintDuration = printInterval;
-
- webRtcPlayerObj.onAggregatedStats = (aggregatedStats) => {
- let numberFormat = new Intl.NumberFormat(window.navigator.language, {
- maximumFractionDigits: 0
- });
- let timeFormat = new Intl.NumberFormat(window.navigator.language, {
- maximumFractionDigits: 0,
- minimumIntegerDigits: 2
- });
-
- // Calculate duration of run
- let runTime = (aggregatedStats.timestamp - aggregatedStats.timestampStart) / 1000;
- let timeValues = [];
- let timeDurations = [60, 60];
- for (let timeIndex = 0; timeIndex < timeDurations.length; timeIndex++) {
- timeValues.push(runTime % timeDurations[timeIndex]);
- runTime = runTime / timeDurations[timeIndex];
- }
- timeValues.push(runTime);
-
- let runTimeSeconds = timeValues[0];
- let runTimeMinutes = Math.floor(timeValues[1]);
- let runTimeHours = Math.floor([timeValues[2]]);
-
- let receivedBytesMeasurement = 'B';
- let receivedBytes = aggregatedStats.hasOwnProperty('bytesReceived') ? aggregatedStats.bytesReceived : 0;
- let dataMeasurements = ['kB', 'MB', 'GB'];
- for (let index = 0; index < dataMeasurements.length; index++) {
- if (receivedBytes < 100 * 1000)
- break;
- receivedBytes = receivedBytes / 1000;
- receivedBytesMeasurement = dataMeasurements[index];
- }
-
- let qualityStatus = document.getElementById("connectionStrength");
- // "blinks" quality status element for 1 sec by making it transparent, speed = number of blinks
- let blinkQualityStatus = function (speed) {
- let iter = speed;
- let opacity = 1; // [0..1]
- let tickId = setInterval(
- function () {
- opacity -= 0.1;
- // map `opacity` to [-0.5..0.5] range, decrement by 0.2 per step and take `abs` to make it blink: 1 -> 0 -> 1
- qualityStatus.style.opacity = `${Math.abs((opacity - 0.5) * 2)}`;
- if (opacity <= 0.1) {
- if (--iter == 0) {
- clearInterval(tickId);
- } else { // next blink
- opacity = 1;
- }
- }
- },
- 100 / speed // msecs
- );
- };
-
- const orangeQP = 26;
- const redQP = 35;
-
- let statsText = '';
- let color;
-
- // Wifi strength elements
-
- if (VideoEncoderQP > redQP) {
-
-
- } else if (VideoEncoderQP > orangeQP) {
-
- } else {
- }
- statsText += `
Duration: ${timeFormat.format(runTimeHours)}:${timeFormat.format(runTimeMinutes)}:${timeFormat.format(runTimeSeconds)}
`;
- statsText += `
Controls stream input: ${inputController === null ? "Not sent yet" : (inputController ? "true" : "false")}
`;
- statsText += `
Audio codec: ${aggregatedStats.hasOwnProperty('audioCodec') ? aggregatedStats.audioCodec : "Not set"}
`;
- statsText += `
Video codec: ${aggregatedStats.hasOwnProperty('videoCodec') ? aggregatedStats.videoCodec : "Not set"}
`;
- statsText += `
Video Resolution: ${aggregatedStats.hasOwnProperty('frameWidth') && aggregatedStats.frameWidth && aggregatedStats.hasOwnProperty('frameHeight') && aggregatedStats.frameHeight ?
- aggregatedStats.frameWidth + 'x' + aggregatedStats.frameHeight : 'Chrome only'
- }
`;
- statsText += `
Received (${receivedBytesMeasurement}): ${numberFormat.format(receivedBytes)}
`;
- statsText += `
Frames Decoded: ${aggregatedStats.hasOwnProperty('framesDecoded') ? numberFormat.format(aggregatedStats.framesDecoded) : 'Chrome only'}
`;
- statsText += `
Packets Lost: ${aggregatedStats.hasOwnProperty('packetsLost') ? numberFormat.format(aggregatedStats.packetsLost) : 'Chrome only'}
`;
- statsText += `
Framerate: ${aggregatedStats.hasOwnProperty('framerate') ? numberFormat.format(aggregatedStats.framerate) : 'Chrome only'}
`;
- statsText += `
Frames dropped: ${aggregatedStats.hasOwnProperty('framesDropped') ? numberFormat.format(aggregatedStats.framesDropped) : 'Chrome only'}
`;
- statsText += `
Net RTT (ms): ${aggregatedStats.hasOwnProperty('currentRoundTripTime') ? numberFormat.format(aggregatedStats.currentRoundTripTime * 1000) : 'Can\'t calculate'}
`;
- statsText += `
Browser receive to composite (ms): ${aggregatedStats.hasOwnProperty('receiveToCompositeMs') ? numberFormat.format(aggregatedStats.receiveToCompositeMs) : 'Chrome only'}
`;
- statsText += `
Audio Bitrate (kbps): ${aggregatedStats.hasOwnProperty('audioBitrate') ? numberFormat.format(aggregatedStats.audioBitrate) : 'Chrome only'}
`;
- statsText += `
Video Bitrate (kbps): ${aggregatedStats.hasOwnProperty('bitrate') ? numberFormat.format(aggregatedStats.bitrate) : 'Chrome only'}
`;
- statsText += `
Video Quantization Parameter: ${VideoEncoderQP}
`;
-
- let statsDiv = document.getElementById("stats");
- statsDiv.innerHTML = statsText;
-
- if (print_stats) {
- if (aggregatedStats.timestampStart) {
- if ((aggregatedStats.timestamp - aggregatedStats.timestampStart) > nextPrintDuration) {
- if (ws && ws.readyState === WS_OPEN_STATE) {
- console.log(`-> SS: stats\n${JSON.stringify(aggregatedStats)}`);
- ws.send(JSON.stringify({
- type: 'stats',
- data: aggregatedStats
- }));
- }
- nextPrintDuration += printInterval;
- }
- }
- }
- };
-
- webRtcPlayerObj.latencyTestTimings.OnAllLatencyTimingsReady = function (timings) {
-
- if (!timings.BrowserReceiptTimeMs) {
- return;
- }
-
- let latencyExcludingDecode = timings.BrowserReceiptTimeMs - timings.TestStartTimeMs;
- let encodeLatency = timings.UEEncodeMs;
- let uePixelStreamLatency = timings.UECaptureToSendMs;
- let ueTestDuration = timings.UETransmissionTimeMs - timings.UEReceiptTimeMs;
- let networkLatency = latencyExcludingDecode - ueTestDuration;
-
- //these ones depend on FrameDisplayDeltaTimeMs
- let endToEndLatency = null;
- let browserSideLatency = null;
-
- if (timings.FrameDisplayDeltaTimeMs && timings.BrowserReceiptTimeMs) {
- endToEndLatency = timings.FrameDisplayDeltaTimeMs + networkLatency + (typeof uePixelStreamLatency === "string" ? 0 : uePixelStreamLatency);
- browserSideLatency = timings.FrameDisplayDeltaTimeMs + (latencyExcludingDecode - networkLatency - ueTestDuration);
- }
-
- let latencyStatsInnerHTML = '';
- latencyStatsInnerHTML += `
Net latency RTT (ms): ${networkLatency.toFixed(2)}
`;
- latencyStatsInnerHTML += `
UE Encode (ms): ${(typeof encodeLatency === "string" ? encodeLatency : encodeLatency.toFixed(2))}
`;
- latencyStatsInnerHTML += `
UE Send to capture (ms): ${(typeof uePixelStreamLatency === "string" ? uePixelStreamLatency : uePixelStreamLatency.toFixed(2))}
`;
- latencyStatsInnerHTML += `
UE probe duration (ms): ${ueTestDuration.toFixed(2)}
`;
- latencyStatsInnerHTML += timings.FrameDisplayDeltaTimeMs && timings.BrowserReceiptTimeMs ? `
Browser composite latency (ms): ${timings.FrameDisplayDeltaTimeMs.toFixed(2)}
` : "";
- latencyStatsInnerHTML += browserSideLatency ? `
Total browser latency (ms): ${browserSideLatency.toFixed(2)}
` : "";
- latencyStatsInnerHTML += endToEndLatency ? `
Total latency (ms): ${endToEndLatency.toFixed(2)}
` : "";
- document.getElementById("LatencyStats").innerHTML = latencyStatsInnerHTML;
- }
-}
-
-function onWebRtcOffer(webRTCData) {
- webRtcPlayerObj.receiveOffer(webRTCData);
- showLoader()
- setupStats();
-}
-
-function onWebRtcAnswer(webRTCData) {
- webRtcPlayerObj.receiveAnswer(webRTCData);
- setupStats();
-}
-
-function onWebRtcSFUPeerDatachannels(webRTCData) {
- webRtcPlayerObj.receiveSFUPeerDataChannelRequest(webRTCData);
-}
-
-function onWebRtcIce(iceCandidate) {
- if (webRtcPlayerObj) {
- webRtcPlayerObj.handleCandidateFromServer(iceCandidate);
- }
-}
-
-let styleWidth;
-let styleHeight;
-let styleTop;
-let styleLeft;
-let styleCursor = 'default';
-let styleAdditional;
-
-const ControlSchemeType = {
- // A mouse can lock inside the WebRTC player so the user can simply move the
- // mouse to control the orientation of the camera. The user presses the
- // Escape key to unlock the mouse.
- LockedMouse: 0,
-
- // A mouse can hover over the WebRTC player so the user needs to click and
- // drag to control the orientation of the camera.
- HoveringMouse: 1
-};
-
-let inputOptions = {
- // The control scheme controls the behaviour of the mouse when it interacts
- // with the WebRTC player.
- controlScheme: ControlSchemeType.LockedMouse,
-
- // Browser keys are those which are typically used by the browser UI. We
- // usually want to suppress these to allow, for example, UE to show shader
- // complexity with the F5 key without the web page refreshing.
- suppressBrowserKeys: true,
-
- // UE has a faketouches option which fakes a single finger touch when the
- // user drags with their mouse. We may perform the reverse; a single finger
- // touch may be converted into a mouse drag UE side. This allows a
- // non-touch application to be controlled partially via a touch device.
- fakeMouseWithTouches: false,
-
- // Hiding the browser cursor enables the use of UE's inbuilt software cursor,
- // without having the browser cursor display on top
- hideBrowserCursor: false
-};
-
-function resizePlayerStyleToFillWindow(playerElement) {
- let videoElement = playerElement.getElementsByTagName("VIDEO");
-
- // Fill the player display in window, keeping picture's aspect ratio.
- let windowAspectRatio = window.innerHeight / window.innerWidth;
- let playerAspectRatio = playerElement.clientHeight / playerElement.clientWidth;
- // We want to keep the video ratio correct for the video stream
- let videoAspectRatio = videoElement.videoHeight / videoElement.videoWidth;
- if (isNaN(videoAspectRatio)) {
- //Video is not initialised yet so set playerElement to size of window
- styleWidth = window.innerWidth;
- styleHeight = window.innerHeight;
- styleTop = 0;
- styleLeft = 0;
- playerElement.style = "top: " + styleTop + "px; left: " + styleLeft + "px; width: " + styleWidth + "px; height: " + styleHeight + "px; cursor: " + styleCursor + "; " + styleAdditional;
- } else if (windowAspectRatio < playerAspectRatio) {
- // Window height is the constraining factor so to keep aspect ratio change width appropriately
- styleWidth = Math.floor(window.innerHeight / videoAspectRatio);
- styleHeight = window.innerHeight;
- styleTop = 0;
- styleLeft = Math.floor((window.innerWidth - styleWidth) * 0.5);
- //Video is now 100% of the playerElement, so set the playerElement style
- playerElement.style = "top: " + styleTop + "px; left: " + styleLeft + "px; width: " + styleWidth + "px; height: " + styleHeight + "px; cursor: " + styleCursor + "; " + styleAdditional;
- } else {
- // Window width is the constraining factor so to keep aspect ratio change height appropriately
- styleWidth = window.innerWidth;
- styleHeight = Math.floor(window.innerWidth * videoAspectRatio);
- styleTop = Math.floor((window.innerHeight - styleHeight) * 0.5);
- styleLeft = 0;
- //Video is now 100% of the playerElement, so set the playerElement style
- playerElement.style = "top: " + styleTop + "px; left: " + styleLeft + "px; width: " + styleWidth + "px; height: " + styleHeight + "px; cursor: " + styleCursor + "; " + styleAdditional;
- }
-}
-
-function resizePlayerStyleToActualSize(playerElement) {
- let videoElement = playerElement.getElementsByTagName("VIDEO");
-
- if (videoElement.length > 0) {
- // Display image in its actual size
- styleWidth = videoElement[0].videoWidth;
- styleHeight = videoElement[0].videoHeight;
- let Top = Math.floor((window.innerHeight - styleHeight) * 0.5);
- let Left = Math.floor((window.innerWidth - styleWidth) * 0.5);
- styleTop = (Top > 0) ? Top : 0;
- styleLeft = (Left > 0) ? Left : 0;
- //Video is now 100% of the playerElement, so set the playerElement style
- playerElement.style = "top: " + styleTop + "px; left: " + styleLeft + "px; width: " + styleWidth + "px; height: " + styleHeight + "px; cursor: " + styleCursor + "; " + styleAdditional;
- }
-}
-
-function resizePlayerStyleToArbitrarySize(playerElement) {
- let videoElement = playerElement.getElementsByTagName("VIDEO");
- //Video is now 100% of the playerElement, so set the playerElement style
- playerElement.style = "top: 0px; left: 0px; width: " + styleWidth + "px; height: " + styleHeight + "px; cursor: " + styleCursor + "; " + styleAdditional;
-}
-
-function setupFreezeFrameOverlay() {
- freezeFrameOverlay = document.createElement('div');
- freezeFrameOverlay.id = 'freezeFrameOverlay';
- freezeFrameOverlay.style.display = 'none';
- freezeFrameOverlay.style.pointerEvents = 'none';
- freezeFrameOverlay.style.position = 'absolute';
- freezeFrameOverlay.style.zIndex = '20';
-
- let freezeFrameImage = document.createElement('img');
- freezeFrameImage.style.position = 'absolute';
- freezeFrameOverlay.appendChild(freezeFrameImage);
-}
-
-function showFreezeFrameOverlay() {
- if (freezeFrame.valid) {
- freezeFrameOverlay.classList.add("freezeframeBackground");
- freezeFrameOverlay.style.display = 'block';
- }
-}
-
-function invalidateFreezeFrameOverlay() {
- setTimeout(() => {
- freezeFrameOverlay.style.display = 'none';
- freezeFrame.valid = false;
- freezeFrameOverlay.classList.remove("freezeframeBackground");
- }, freezeFrameDelay);
-
- if (webRtcPlayerObj) {
- webRtcPlayerObj.setVideoEnabled(true);
- }
-}
-
-function resizeFreezeFrameOverlay() {
- if (freezeFrame.width !== 0 && freezeFrame.height !== 0) {
- let displayWidth = 0;
- let displayHeight = 0;
- let displayTop = 0;
- let displayLeft = 0;
- let checkBox = document.getElementById('enlarge-display-to-fill-window-tgl');
- let playerElement = document.getElementById('player');
- if (checkBox !== null && checkBox.checked) {
- // We are fitting video to screen, we care about the screen (window) size
- let windowAspectRatio = window.innerWidth / window.innerHeight;
- let videoAspectRatio = freezeFrame.width / freezeFrame.height;
- if (windowAspectRatio < videoAspectRatio) {
- displayWidth = window.innerWidth;
- displayHeight = Math.floor(window.innerWidth / videoAspectRatio);
- displayTop = Math.floor((window.innerHeight - displayHeight) * 0.5);
- displayLeft = 0;
- } else {
- displayWidth = Math.floor(window.innerHeight * videoAspectRatio);
- displayHeight = window.innerHeight;
- displayTop = 0;
- displayLeft = Math.floor((window.innerWidth - displayWidth) * 0.5);
- }
- } else {
- // Video is coming in at native resolution, we care more about the player size
- let playerAspectRatio = playerElement.offsetWidth / playerElement.offsetHeight;
- let videoAspectRatio = freezeFrame.width / freezeFrame.height;
- if (playerAspectRatio < videoAspectRatio) {
- displayWidth = playerElement.offsetWidth;
- displayHeight = Math.floor(playerElement.offsetWidth / videoAspectRatio);
- displayTop = Math.floor((playerElement.offsetHeight - displayHeight) * 0.5);
- displayLeft = 0;
- } else {
- displayWidth = Math.floor(playerElement.offsetHeight * videoAspectRatio);
- displayHeight = playerElement.offsetHeight;
- displayTop = 0;
- displayLeft = Math.floor((playerElement.offsetWidth - displayWidth) * 0.5);
- }
- }
- let freezeFrameImage = document.getElementById("freezeFrameOverlay").childNodes[0];
- freezeFrameOverlay.style.width = playerElement.offsetWidth + 'px';
- freezeFrameOverlay.style.height = playerElement.offsetHeight + 'px';
- freezeFrameOverlay.style.left = 0 + 'px';
- freezeFrameOverlay.style.top = 0 + 'px';
-
- freezeFrameImage.style.width = displayWidth + 'px';
- freezeFrameImage.style.height = displayHeight + 'px';
- freezeFrameImage.style.left = displayLeft + 'px';
- freezeFrameImage.style.top = displayTop + 'px';
- }
-}
-
-function resizePlayerStyle(event) {
- let playerElement = document.getElementById('player');
-
- if (!playerElement)
- return;
-
- updateVideoStreamSize();
-
- if (playerElement.classList.contains('fixed-size')) {
- setupMouseAndFreezeFrame(playerElement)
- return;
- }
-
-
- let checkBox = document.getElementById('enlarge-display-to-fill-window-tgl');
- let windowSmallerThanPlayer = window.innerWidth < playerElement.videoWidth || window.innerHeight < playerElement.videoHeight;
- if (checkBox !== null) {
- if (checkBox.checked || windowSmallerThanPlayer) {
- resizePlayerStyleToFillWindow(playerElement);
- } else {
- resizePlayerStyleToActualSize(playerElement);
- }
- } else {
- resizePlayerStyleToArbitrarySize(playerElement);
- }
-
- setupMouseAndFreezeFrame(playerElement)
-}
-
-function setupMouseAndFreezeFrame(playerElement) {
- // Calculating and normalizing positions depends on the width and height of
- // the player.
- playerElementClientRect = playerElement.getBoundingClientRect();
- setupNormalizeAndQuantize();
- resizeFreezeFrameOverlay();
-}
-
-function updateVideoStreamSize() {
- if (!matchViewportResolution) {
- return;
- }
-
- let now = new Date().getTime();
- if (now - lastTimeResized > 1000) {
- let playerElement = document.getElementById('player');
- if (!playerElement)
- return;
-
- let descriptor = {
- "Resolution.Width": playerElement.clientWidth,
- "Resolution.Height": playerElement.clientHeight
- };
- emitCommand(descriptor);
- console.log(descriptor);
- lastTimeResized = new Date().getTime();
- } else {
- console.log('Resizing too often - skipping');
- clearTimeout(resizeTimeout);
- resizeTimeout = setTimeout(updateVideoStreamSize, 1000);
- }
-}
-
-// Fix for bug in iOS where windowsize is not correct at instance or orientation change
-// https://github.com/dimsemenov/PhotoSwipe/issues/1315
-let _orientationChangeTimeout;
-
-function onOrientationChange(event) {
- clearTimeout(_orientationChangeTimeout);
- _orientationChangeTimeout = setTimeout(function () {
- resizePlayerStyle();
- }, 500);
-}
-
-function sendMessageToStreamer(messageType, indata = []) {
- let messageFormat = toStreamerMessages.getFromKey(messageType);
- if (messageFormat === undefined) {
- console.error(`Attempted to send a message to the streamer with message type: ${messageType}, but the frontend hasn't been configured to send such a message. Check you've added the message type in your cpp`);
- return;
- }
- // console.log(`Calculate size: ${new Blob(JSON.stringify(indata)).size}, Specified size: ${messageFormat.byteLength}`);
- let data = new DataView(new ArrayBuffer(messageFormat.byteLength + 1));
-
- data.setUint8(0, messageFormat.id);
- let byteOffset = 1;
-
- indata.forEach((element, idx) => {
- let type = messageFormat.structure[idx];
- switch (type) {
- case "uint8":
- data.setUint8(byteOffset, element);
- byteOffset += 1;
- break;
-
- case "uint16":
- data.setUint16(byteOffset, element, true);
- byteOffset += 2;
- break;
-
- case "int16":
- data.setInt16(byteOffset, element, true);
- byteOffset += 2;
- break;
-
- case "double":
- data.setFloat64(byteOffset, element, true);
- byteOffset += 8;
- break;
- }
- });
- sendInputData(data.buffer);
-}
-
-// A generic message has a type and a descriptor.
-function emitDescriptor(messageType, descriptor) {
- console.log(messageType, descriptor)
- // Convert the descriptor object into a JSON string.
- let descriptorAsString = JSON.stringify(descriptor);
- let messageFormat = toStreamerMessages.getFromKey(messageType);
- if (messageFormat === undefined) {
- console.error(`Attempted to emit descriptor with message type: ${messageType}, but the frontend hasn't been configured to send such a message. Check you've added the message type in your cpp`);
- }
- // Add the UTF-16 JSON string to the array byte buffer, going two bytes at
- // a time.
- let data = new DataView(new ArrayBuffer(1 + 2 + 2 * descriptorAsString.length));
- let byteIdx = 0;
- data.setUint8(byteIdx, messageFormat.id);
- byteIdx++;
- data.setUint16(byteIdx, descriptorAsString.length, true);
- byteIdx += 2;
- for (let i = 0; i < descriptorAsString.length; i++) {
- data.setUint16(byteIdx, descriptorAsString.charCodeAt(i), true);
- byteIdx += 2;
- }
- sendInputData(data.buffer);
-}
-
-// A built-in command can be sent to UE client. The commands are defined by a
-// JSON descriptor and will be executed automatically.
-// The currently supported commands are:
-//
-// 1. A command to run any console command:
-// "{ ConsoleCommand:
}"
-//
-// 2. A command to change the resolution to the given width and height.
-// "{ Resolution.Width: , Resolution.Height: } }"
-//
-function emitCommand(descriptor) {
- emitDescriptor("Command", descriptor);
-}
-
-// A UI interation will occur when the user presses a button powered by
-// JavaScript as opposed to pressing a button which is part of the pixel
-// streamed UI from the UE client.
-function emitUIInteraction(descriptor) {
- emitDescriptor("UIInteraction", descriptor);
-}
-
-function requestInitialSettings() {
- sendMessageToStreamer("RequestInitialSettings");
-}
-
-function requestQualityControl() {
- if (!qualityController) {
- sendMessageToStreamer("RequestQualityControl");
- }
-}
-
-let playerElementClientRect = undefined;
-let normalizeAndQuantizeUnsigned = undefined;
-let normalizeAndQuantizeSigned = undefined;
-let unquantizeAndDenormalizeUnsigned = undefined;
-
-function setupNormalizeAndQuantize() {
- let playerElement = document.getElementById('player');
- let videoElement = playerElement.getElementsByTagName("video");
-
- if (playerElement && videoElement.length > 0) {
- let playerAspectRatio = playerElement.clientHeight / playerElement.clientWidth;
- let videoAspectRatio = videoElement[0].videoHeight / videoElement[0].videoWidth;
-
- // Unsigned XY positions are the ratio (0.0..1.0) along a viewport axis,
- // quantized into an uint16 (0..65536).
- // Signed XY deltas are the ratio (-1.0..1.0) along a viewport axis,
- // quantized into an int16 (-32767..32767).
- // This allows the browser viewport and client viewport to have a different
- // size.
- // Hack: Currently we set an out-of-range position to an extreme (65535)
- // as we can't yet accurately detect mouse enter and leave events
- // precisely inside a video with an aspect ratio which causes mattes.
- if (playerAspectRatio > videoAspectRatio) {
- if (print_inputs) {
- console.log('Setup Normalize and Quantize for playerAspectRatio > videoAspectRatio');
- }
- let ratio = playerAspectRatio / videoAspectRatio;
- // Unsigned.
- normalizeAndQuantizeUnsigned = (x, y) => {
- let normalizedX = x / playerElement.clientWidth;
- let normalizedY = ratio * (y / playerElement.clientHeight - 0.5) + 0.5;
- if (normalizedX < 0.0 || normalizedX > 1.0 || normalizedY < 0.0 || normalizedY > 1.0) {
- return {
- inRange: false,
- x: 65535,
- y: 65535
- };
- } else {
- return {
- inRange: true,
- x: normalizedX * 65536,
- y: normalizedY * 65536
- };
- }
- };
- unquantizeAndDenormalizeUnsigned = (x, y) => {
- let normalizedX = x / 65536;
- let normalizedY = (y / 65536 - 0.5) / ratio + 0.5;
- return {
- x: normalizedX * playerElement.clientWidth,
- y: normalizedY * playerElement.clientHeight
- };
- };
- // Signed.
- normalizeAndQuantizeSigned = (x, y) => {
- let normalizedX = x / (0.5 * playerElement.clientWidth);
- let normalizedY = (ratio * y) / (0.5 * playerElement.clientHeight);
- return {
- x: normalizedX * 32767,
- y: normalizedY * 32767
- };
- };
- } else {
- if (print_inputs) {
- console.log('Setup Normalize and Quantize for playerAspectRatio <= videoAspectRatio');
- }
- let ratio = videoAspectRatio / playerAspectRatio;
- // Unsigned.
- normalizeAndQuantizeUnsigned = (x, y) => {
- let normalizedX = ratio * (x / playerElement.clientWidth - 0.5) + 0.5;
- let normalizedY = y / playerElement.clientHeight;
- if (normalizedX < 0.0 || normalizedX > 1.0 || normalizedY < 0.0 || normalizedY > 1.0) {
- return {
- inRange: false,
- x: 65535,
- y: 65535
- };
- } else {
- return {
- inRange: true,
- x: normalizedX * 65536,
- y: normalizedY * 65536
- };
- }
- };
- unquantizeAndDenormalizeUnsigned = (x, y) => {
- let normalizedX = (x / 65536 - 0.5) / ratio + 0.5;
- let normalizedY = y / 65536;
- return {
- x: normalizedX * playerElement.clientWidth,
- y: normalizedY * playerElement.clientHeight
- };
- };
- // Signed.
- normalizeAndQuantizeSigned = (x, y) => {
- let normalizedX = (ratio * x) / (0.5 * playerElement.clientWidth);
- let normalizedY = y / (0.5 * playerElement.clientHeight);
- return {
- x: normalizedX * 32767,
- y: normalizedY * 32767
- };
- };
- }
- }
-}
-
-// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
-const MouseButton = {
- MainButton: 0, // Left button.
- AuxiliaryButton: 1, // Wheel button.
- SecondaryButton: 2, // Right button.
- FourthButton: 3, // Browser Back button.
- FifthButton: 4 // Browser Forward button.
-};
-
-// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
-const MouseButtonsMask = {
- PrimaryButton: 1, // Left button.
- SecondaryButton: 2, // Right button.
- AuxiliaryButton: 4, // Wheel button.
- FourthButton: 8, // Browser Back button.
- FifthButton: 16 // Browser Forward button.
-};
-
-// If the user has any mouse buttons pressed then release them.
-function releaseMouseButtons(buttons, x, y) {
- let coord = normalizeAndQuantizeUnsigned(x, y);
- if (buttons & MouseButtonsMask.PrimaryButton) {
- toStreamerHandlers.MouseUp("MouseUp", [MouseButton.MainButton, coord.x, coord.y]);
- }
- if (buttons & MouseButtonsMask.SecondaryButton) {
- toStreamerHandlers.MouseUp("MouseUp", [MouseButton.SecondaryButton, coord.x, coord.y]);
- }
- if (buttons & MouseButtonsMask.AuxiliaryButton) {
- toStreamerHandlers.MouseUp("MouseUp", [MouseButton.AuxiliaryButton, coord.x, coord.y]);
- }
- if (buttons & MouseButtonsMask.FourthButton) {
- toStreamerHandlers.MouseUp("MouseUp", [MouseButton.FourthButton, coord.x, coord.y]);
- }
- if (buttons & MouseButtonsMask.FifthButton) {
- toStreamerHandlers.MouseUp("MouseUp", [MouseButton.FifthButton, coord.x, coord.y]);
- }
-}
-
-// If the user has any Mouse buttons pressed then press them again.
-function pressMouseButtons(buttons, x, y) {
- let coord = normalizeAndQuantizeUnsigned(x, y);
- if (buttons & MouseButtonsMask.PrimaryButton) {
- toStreamerHandlers.MouseDown("MouseDown", [MouseButton.MainButton, coord.x, coord.y]);
- }
- if (buttons & MouseButtonsMask.SecondaryButton) {
- toStreamerHandlers.MouseDown("MouseDown", [MouseButton.SecondaryButton, coord.x, coord.y]);
- }
- if (buttons & MouseButtonsMask.AuxiliaryButton) {
- toStreamerHandlers.MouseDown("MouseDown", [MouseButton.AuxiliaryButton, coord.x, coord.y]);
- }
- if (buttons & MouseButtonsMask.FourthButton) {
- toStreamerHandlers.MouseDown("MouseDown", [MouseButton.FourthButton, coord.x, coord.y]);
- }
- if (buttons & MouseButtonsMask.FifthButton) {
- toStreamerHandlers.MouseDown("MouseDown", [MouseButton.FifthButton, coord.x, coord.y]);
- }
-}
-
-function registerInputs(playerElement) {
- if (!playerElement)
- return;
-
- registerMouseEnterAndLeaveEvents(playerElement);
- registerTouchEvents(playerElement);
-}
-
-function createOnScreenKeyboardHelpers(htmlElement) {
- if (document.getElementById('hiddenInput') === null) {
- hiddenInput = document.createElement('input');
- hiddenInput.id = 'hiddenInput';
- hiddenInput.maxLength = 0;
- htmlElement.appendChild(hiddenInput);
- }
-
- if (document.getElementById('editTextButton') === null) {
- editTextButton = document.createElement('button');
- editTextButton.id = 'editTextButton';
- editTextButton.innerHTML = 'edit text';
- htmlElement.appendChild(editTextButton);
-
- // Hide the 'edit text' button.
- editTextButton.classList.add('hiddenState');
-
- editTextButton.addEventListener('click', function () {
- // Show the on-screen keyboard.
- hiddenInput.focus();
- });
- }
-}
-
-function showOnScreenKeyboard(command) {
- if (command.showOnScreenKeyboard) {
- // Show the 'edit text' button.
- editTextButton.classList.remove('hiddenState');
- // Place the 'edit text' button near the UE input widget.
- let pos = unquantizeAndDenormalizeUnsigned(command.x, command.y);
- editTextButton.style.top = pos.y.toString() + 'px';
- editTextButton.style.left = (pos.x - 40).toString() + 'px';
- } else {
- // Hide the 'edit text' button.
- editTextButton.classList.add('hiddenState');
- // Hide the on-screen keyboard.
- hiddenInput.blur();
- }
-}
-
-function registerMouseEnterAndLeaveEvents(playerElement) {
- playerElement.onmouseenter = function (e) {
- if (print_inputs) {
- console.log('mouse enter');
- }
- toStreamerHandlers.MouseEnter("MouseEnter");
- playerElement.pressMouseButtons(e);
- };
-
- playerElement.onmouseleave = function (e) {
- if (print_inputs) {
- console.log('mouse leave');
- }
- toStreamerHandlers.MouseLeave("MouseLeave");
- playerElement.releaseMouseButtons(e);
- };
-}
-
-// A locked mouse works by the user clicking in the browser player and the
-// cursor disappears and is locked. The user moves the cursor and the camera
-// moves, for example. The user presses escape to free the mouse.
-function registerLockedMouseEvents(playerElement) {
- styleCursor = (inputOptions.hideBrowserCursor ? 'none' : 'default');
- let x = playerElement.width / 2;
- let y = playerElement.height / 2;
- let coord = normalizeAndQuantizeUnsigned(x, y);
-
- playerElement.requestPointerLock = playerElement.requestPointerLock || playerElement.mozRequestPointerLock;
- document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock;
-
- playerElement.onclick = function () {
- playerElement.requestPointerLock();
- };
-
- // Respond to lock state change events
- document.addEventListener('pointerlockchange', lockStateChange, false);
- document.addEventListener('mozpointerlockchange', lockStateChange, false);
-
- function lockStateChange() {
- if (document.pointerLockElement === playerElement ||
- document.mozPointerLockElement === playerElement) {
- console.log('Pointer locked');
- document.addEventListener("mousemove", updatePosition, false);
- } else {
- console.log('The pointer lock status is now unlocked');
- document.removeEventListener("mousemove", updatePosition, false);
-
- // If mouse loses focus, send a key up for all of the currently held-down keys
- // This is necessary as when the mouse loses focus, the windows stops listening for events and as such
- // the keyup listener won't get fired
- [...new Set(activeKeys)].forEach((uniqueKeycode) => {
- toStreamerHandlers.KeyUp("KeyUp", [uniqueKeycode]);
- });
- // Reset the active keys back to nothing
- activeKeys = [];
- }
- }
-
- function updatePosition(e) {
- x += e.movementX;
- y += e.movementY;
- if (x > styleWidth) {
- x -= styleWidth;
- }
- if (y > styleHeight) {
- y -= styleHeight;
- }
- if (x < 0) {
- x = styleWidth + x;
- }
- if (y < 0) {
- y = styleHeight - y;
- }
-
- let coord = normalizeAndQuantizeUnsigned(x, y);
- let delta = normalizeAndQuantizeSigned(e.movementX, e.movementY);
- toStreamerHandlers.MouseMove("MouseMove", [coord.x, coord.y, delta.x, delta.y]);
- }
-
-
- playerElement.onmousedown = function (e) {
- toStreamerHandlers.MouseDown("MouseDown", [e.button, coord.x, coord.y]);
- };
-
- playerElement.onmouseup = function (e) {
- toStreamerHandlers.MouseUp("MouseUp", [e.button, coord.x, coord.y]);
- };
-
- playerElement.onwheel = function (e) {
- toStreamerHandlers.MouseWheel("MouseWheel", [e.wheelDelta, coord.x, coord.y]);
- };
-
- playerElement.ondblclick = function (e) {
- toStreamerHandlers.MouseDown("MouseDouble", [e.button, coord.x, coord.y]);
- };
-
- playerElement.pressMouseButtons = function (e) {
- pressMouseButtons(e.buttons, x, y);
- };
-
- playerElement.releaseMouseButtons = function (e) {
- releaseMouseButtons(e.buttons, x, y);
- };
-}
-
-// A hovering mouse works by the user clicking the mouse button when they want
-// the cursor to have an effect over the video. Otherwise the cursor just
-// passes over the browser.
-function registerHoveringMouseEvents(playerElement) {
- styleCursor = (inputOptions.hideBrowserCursor ? 'none' : 'default');
-
- playerElement.onmousemove = function (e) {
- let coord = normalizeAndQuantizeUnsigned(e.offsetX, e.offsetY);
- let delta = normalizeAndQuantizeSigned(e.movementX, e.movementY);
- toStreamerHandlers.MouseMove("MouseMove", [coord.x, coord.y, delta.x, delta.y]);
- e.preventDefault();
- };
-
- playerElement.onmousedown = function (e) {
- let coord = normalizeAndQuantizeUnsigned(e.offsetX, e.offsetY);
- toStreamerHandlers.MouseDown("MouseDown", [e.button, coord.x, coord.y]);
- e.preventDefault();
- };
-
- playerElement.onmouseup = function (e) {
- let coord = normalizeAndQuantizeUnsigned(e.offsetX, e.offsetY);
- toStreamerHandlers.MouseUp("MouseUp", [e.button, coord.x, coord.y]);
- e.preventDefault();
- };
-
- // When the context menu is shown then it is safest to release the button
- // which was pressed when the event happened. This will guarantee we will
- // get at least one mouse up corresponding to a mouse down event. Otherwise
- // the mouse can get stuck.
- // https://github.com/facebook/react/issues/5531
- playerElement.oncontextmenu = function (e) {
- let coord = normalizeAndQuantizeUnsigned(e.offsetX, e.offsetY);
- toStreamerHandlers.MouseUp("MouseUp", [e.button, coord.x, coord.y]);
- e.preventDefault();
- };
-
- playerElement.onwheel = function (e) {
- let coord = normalizeAndQuantizeUnsigned(e.offsetX, e.offsetY);
- toStreamerHandlers.MouseWheel("MouseWheel", [e.wheelDelta, coord.x, coord.y]);
- e.preventDefault();
- };
-
- playerElement.ondblclick = function (e) {
- let coord = normalizeAndQuantizeUnsigned(e.offsetX, e.offsetY);
- toStreamerHandlers.MouseDown("MouseDouble", [e.button, coord.x, coord.y]);
- };
-
- playerElement.pressMouseButtons = function (e) {
- pressMouseButtons(e.buttons, e.offsetX, e.offsetY);
- };
-
- playerElement.releaseMouseButtons = function (e) {
- releaseMouseButtons(e.buttons, e.offsetX, e.offsetY);
- };
-}
-
-function registerTouchEvents(playerElement) {
- // We need to assign a unique identifier to each finger.
- // We do this by mapping each Touch object to the identifier.
- let fingers = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
- let fingerIds = {};
-
- function rememberTouch(touch) {
- let finger = fingers.pop();
- if (finger === undefined) {
- console.log('exhausted touch indentifiers');
- }
- fingerIds[touch.identifier] = finger;
- }
-
- function forgetTouch(touch) {
- fingers.push(fingerIds[touch.identifier]);
- // Sort array back into descending order. This means if finger '1' were to lift after finger '0', we would ensure that 0 will be the first index to pop
- fingers.sort(function (a, b) { return b - a });
- delete fingerIds[touch.identifier];
- }
-
- function emitTouchData(type, touches) {
- for (let t = 0; t < touches.length; t++) {
- let numTouches = 1; // the number of touches to be sent this message
- let touch = touches[t];
- let x = touch.clientX - playerElement.offsetLeft;
- let y = touch.clientY - playerElement.offsetTop;
- if (print_inputs) {
- console.log(`F${fingerIds[touch.identifier]}=(${x}, ${y})`);
- }
- let coord = normalizeAndQuantizeUnsigned(x, y);
-
- switch (type) {
- case "TouchStart":
- toStreamerHandlers.TouchStart("TouchStart", [numTouches, coord.x, coord.y, fingerIds[touch.identifier], MaxByteValue * touch.force, coord.inRange ? 1 : 0]);
- break;
- case "TouchEnd":
- toStreamerHandlers.TouchStart("TouchEnd", [numTouches, coord.x, coord.y, fingerIds[touch.identifier], MaxByteValue * touch.force, coord.inRange ? 1 : 0]);
- break;
- case "TouchMove":
- toStreamerHandlers.TouchStart("TouchMove", [numTouches, coord.x, coord.y, fingerIds[touch.identifier], MaxByteValue * touch.force, coord.inRange ? 1 : 0]);
- break;
- }
- }
- }
-
- if (inputOptions.fakeMouseWithTouches) {
-
- let finger = undefined;
-
- playerElement.ontouchstart = function (e) {
- if (finger === undefined) {
- let firstTouch = e.changedTouches[0];
- finger = {
- id: firstTouch.identifier,
- x: firstTouch.clientX - playerElementClientRect.left,
- y: firstTouch.clientY - playerElementClientRect.top
- };
- // Hack: Mouse events require an enter and leave so we just
- // enter and leave manually with each touch as this event
- // is not fired with a touch device.
- playerElement.onmouseenter(e);
- let coord = normalizeAndQuantizeUnsigned(finger.x, finger.y);
- toStreamerHandlers.MouseDown("MouseDown", [MouseButton.MainButton, coord.x, coord.y]);
- }
- e.preventDefault();
- };
-
- playerElement.ontouchend = function (e) {
- for (let t = 0; t < e.changedTouches.length; t++) {
- let touch = e.changedTouches[t];
- if (touch.identifier === finger.id) {
- let x = touch.clientX - playerElementClientRect.left;
- let y = touch.clientY - playerElementClientRect.top;
- let coord = normalizeAndQuantizeUnsigned(x, y);
- toStreamerHandlers.MouseUp("MouseUp", [MouseButton.MainButton, coord.x, coord.y]);
- // Hack: Manual mouse leave event.
- playerElement.onmouseleave(e);
- finger = undefined;
- break;
- }
- }
- e.preventDefault();
- };
-
- playerElement.ontouchmove = function (e) {
- for (let t = 0; t < e.touches.length; t++) {
- let touch = e.touches[t];
- if (touch.identifier === finger.id) {
- let x = touch.clientX - playerElementClientRect.left;
- let y = touch.clientY - playerElementClientRect.top;
- let coord = normalizeAndQuantizeUnsigned(x, y);
- let delta = normalizeAndQuantizeSigned(x - finger.x, y - finger.y);
- toStreamerHandlers.MouseMove("MouseMove", [coord.x, coord.y, delta.x, delta.y]);
- finger.x = x;
- finger.y = y;
- break;
- }
- }
- e.preventDefault();
- };
- } else {
- playerElement.ontouchstart = function (e) {
- // Assign a unique identifier to each touch.
- for (let t = 0; t < e.changedTouches.length; t++) {
- rememberTouch(e.changedTouches[t]);
- }
-
- if (print_inputs) {
- console.log('touch start');
- }
- emitTouchData("TouchStart", e.changedTouches);
- e.preventDefault();
- };
-
- playerElement.ontouchend = function (e) {
- if (print_inputs) {
- console.log('touch end');
- }
- emitTouchData("TouchEnd", e.changedTouches);
-
- // Re-cycle unique identifiers previously assigned to each touch.
- for (let t = 0; t < e.changedTouches.length; t++) {
- forgetTouch(e.changedTouches[t]);
- }
- e.preventDefault();
- };
-
- playerElement.ontouchmove = function (e) {
- if (print_inputs) {
- console.log('touch move');
- }
- emitTouchData("TouchMove", e.touches);
- e.preventDefault();
- };
- }
-}
-
-// Browser keys do not have a charCode so we only need to test keyCode.
-function isKeyCodeBrowserKey(keyCode) {
- // Function keys or tab key.
- return keyCode >= 112 && keyCode <= 123 || keyCode === 9;
-}
-
-// Must be kept in sync with JavaScriptKeyCodeToFKey C++ array. The index of the
-// entry in the array is the special key code given below.
-const SpecialKeyCodes = {
- BackSpace: 8,
- Shift: 16,
- Control: 17,
- Alt: 18,
- RightShift: 253,
- RightControl: 254,
- RightAlt: 255
-};
-
-/*
-* New browser APIs have moved away from KeyboarddEvent.keyCode to KeyboardEvent.Code.
-* For details see: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#constants_for_keycode_value
-* We still use old KeyboardEvent.keyCode integers in the UE C++ side, so we need a way to map the new
-* string-based KeyboardEvenet.Code to the old integers.
-*/
-const CodeToKeyCode = {
- "Escape": 27,
- "Digit0": 48,
- "Digit1": 49,
- "Digit2": 50,
- "Digit3": 51,
- "Digit4": 52,
- "Digit5": 53,
- "Digit6": 54,
- "Digit7": 55,
- "Digit8": 56,
- "Digit9": 57,
- "Minus": 173,
- "Equal": 187,
- "Backspace": 8,
- "Tab": 9,
- "KeyQ": 81,
- "KeyW": 87,
- "KeyE": 69,
- "KeyR": 82,
- "KeyT": 84,
- "KeyY": 89,
- "KeyU": 85,
- "KeyI": 73,
- "KeyO": 79,
- "KeyP": 80,
- "BracketLeft": 219,
- "BracketRight": 221,
- "Enter": 13,
- "ControlLeft": 17,
- "KeyA": 65,
- "KeyS": 83,
- "KeyD": 68,
- "KeyF": 70,
- "KeyG": 71,
- "KeyH": 72,
- "KeyJ": 74,
- "KeyK": 75,
- "KeyL": 76,
- "Semicolon": 186,
- "Quote": 222,
- "Backquote": 192,
- "ShiftLeft": 16,
- "Backslash": 220,
- "KeyZ": 90,
- "KeyX": 88,
- "KeyC": 67,
- "KeyV": 86,
- "KeyB": 66,
- "KeyN": 78,
- "KeyM": 77,
- "Comma": 188,
- "Period": 190,
- "Slash": 191,
- "ShiftRight": 253,
- "AltLeft": 18,
- "Space": 32,
- "CapsLock": 20,
- "F1": 112,
- "F2": 113,
- "F3": 114,
- "F4": 115,
- "F5": 116,
- "F6": 117,
- "F7": 118,
- "F8": 119,
- "F9": 120,
- "F10": 121,
- "F11": 122,
- "F12": 123,
- "Pause": 19,
- "ScrollLock": 145,
- "NumpadDivide": 111,
- "NumpadMultiply": 106,
- "NumpadSubtract": 109,
- "NumpadAdd": 107,
- "NumpadDecimal": 110,
- "Numpad9": 105,
- "Numpad8": 104,
- "Numpad7": 103,
- "Numpad6": 102,
- "Numpad5": 101,
- "Numpad4": 100,
- "Numpad3": 99,
- "Numpad2": 98,
- "Numpad1": 97,
- "Numpad0": 96,
- "NumLock": 144,
- "ControlRight": 254,
- "AltRight": 255,
- "Home": 36,
- "End": 35,
- "ArrowUp": 38,
- "ArrowLeft": 37,
- "ArrowRight": 39,
- "ArrowDown": 40,
- "PageUp": 33,
- "PageDown": 34,
- "Insert": 45,
- "Delete": 46,
- "ContextMenu": 93
-};
-
-
-function getKeyCode(e) {
-
- // If we don't have keyCode property because browser API is deprecated then use KeyboardEvent.code instead.
- // See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#constants_for_keycode_value
- if (!("keyCode" in e)) {
- // Convert KeyboardEvent.code string into integer-based key code for backwards compatibility reasons.
- if (e.code in CodeToKeyCode) {
- return CodeToKeyCode[e.code];
- } else {
- console.warn(`Keyboard code of ${e.code} is not supported in our mapping, ignoring this key.`);
- return null;
- }
- }
-
- // If we made it here KeyboardEvent.keyCode is still supported so we can safely use it.
-
- // Below logic is so we can detect and differentiate between left and right versions of some keys.
- if (e.keyCode === SpecialKeyCodes.Shift && e.code === 'ShiftRight') return SpecialKeyCodes.RightShift;
- else if (e.keyCode === SpecialKeyCodes.Control && e.code === 'ControlRight') return SpecialKeyCodes.RightControl;
- else if (e.keyCode === SpecialKeyCodes.Alt && e.code === 'AltRight') return SpecialKeyCodes.RightAlt;
- else return e.keyCode;
-}
-
-function registerKeyboardEvents() {
-
- document.onkeydown = function (e) {
-
- const keyCode = getKeyCode(e);
- if (!keyCode) { return; }
-
- if (print_inputs) {
- console.log(`key down ${keyCode}, repeat = ${e.repeat}`);
- }
-
- toStreamerHandlers.KeyDown("KeyDown", [keyCode, e.repeat]);
- activeKeys.push(keyCode);
-
- // Backspace is not considered a keypress in JavaScript but we need it
- // to be so characters may be deleted in a UE text entry field.
- if (keyCode === SpecialKeyCodes.BackSpace) {
- document.onkeypress({
- charCode: SpecialKeyCodes.BackSpace
- });
- }
- if (inputOptions.suppressBrowserKeys && isKeyCodeBrowserKey(e.keyCode)) {
- e.preventDefault();
- }
-
- };
-
- document.onkeyup = function (e) {
-
- const keyCode = getKeyCode(e);
- if (!keyCode) { return; }
-
- if (print_inputs) {
- console.log(`key up ${keyCode}`);
- }
- toStreamerHandlers.KeyUp("KeyUp", [keyCode, e.repeat]);
-
- // if we are suppressing browser keys and this key is a browser key then cancel it from being used in browser
- if (inputOptions.suppressBrowserKeys && isKeyCodeBrowserKey(keyCode)) {
- e.preventDefault();
- }
- };
-
- document.onkeypress = function (e) {
- if (!("charCode" in e)) {
- console.warn("KeyboardEvent.charCode is deprecated in this browser, cannot send key press.");
- return;
- }
- // @ts-ignore: deprecation is being used safely
- const charCode = e.charCode;
- if (print_inputs) {
- console.log(`key press ${e.charCode}`);
- }
- toStreamerHandlers.KeyPress("KeyPress", [e.charCode]);
- };
-}
-
-function settingsClicked( /* e */) {
- /**
- * Toggle settings panel. If stats panel is already open, close it and then open settings
- */
- let settings = document.getElementById('settings-panel');
- let stats = document.getElementById('stats-panel');
-
- if (stats.classList.contains("panel-wrap-visible")) {
- stats.classList.toggle("panel-wrap-visible");
- }
-
- settings.classList.toggle("panel-wrap-visible");
-}
-
-function statsClicked( /* e */) {
- /**
- * Toggle stats panel. If settings panel is already open, close it and then open stats
- */
- let settings = document.getElementById('settings-panel');
- let stats = document.getElementById('stats-panel');
-
- if (settings.classList.contains("panel-wrap-visible")) {
- settings.classList.toggle("panel-wrap-visible");
- }
-
- stats.classList.toggle("panel-wrap-visible");
-}
-
-
-
-function start(isReconnection) {
- // update "quality status" to "disconnected" state
- let qualityStatus = document.getElementById("qualityStatus");
- if (qualityStatus) {
- qualityStatus.className = "grey-status";
- }
-
-
- let statsDiv = document.getElementById("stats");
- if (statsDiv) {
- statsDiv.innerHTML = 'Not connected';
- }
-
- if (!connect_on_load || isReconnection) {
- showConnectOverlay();
- invalidateFreezeFrameOverlay();
- shouldShowPlayOverlay = true;
- resizePlayerStyle();
- } else {
- connect();
- }
-}
-
-function connect() {
- "use strict";
-
- window.WebSocket = window.WebSocket || window.MozWebSocket;
-
- if (!window.WebSocket) {
- alert('Your browser doesn\'t support WebSocket');
- return;
- }
-
- // Make a new websocket connection
- let connectionUrl = store.getState().sessionReducer.url
- console.log(`Creating a websocket connection to: ${connectionUrl}`);
- ws = new WebSocket(connectionUrl);
- ws.attemptStreamReconnection = true;
-
- ws.onmessagebinary = function (event) {
- if (!event || !event.data) { return; }
-
- event.data.text().then(function (messageString) {
- // send the new stringified event back into `onmessage`
- ws.onmessage({ data: messageString });
- }).catch(function (error) {
- console.error(`Failed to parse binary blob from websocket, reason: ${error}`);
- });
- }
-
-
-
- ws.onmessage = function (event) {
-
- // Check if websocket message is binary, if so, stringify it.
- if (event.data && event.data instanceof Blob) {
- ws.onmessagebinary(event);
- return;
- }
-
- let msg = JSON.parse(event.data);
- if (msg.type === 'config') {
- console.log("%c[Inbound SS (config)]", "background: lightblue; color: black", msg);
- onConfig(msg);
- } else if (msg.type === 'playerCount') {
- usersArray.fill(msg.count)
- console.log("%c[Inbound SS (playerCount)]", "background: lightblue; color: black", msg);
- } else if (msg.type === 'offer') {
- console.log("%c[Inbound SS (offer)]", "background: lightblue; color: black", msg);
- if (!UrlParamsCheck('offerToReceive')) {
- onWebRtcOffer(msg);
- }
- } else if (msg.type === 'answer') {
- console.log("%c[Inbound SS (answer)]", "background: lightblue; color: black", msg);
- onWebRtcAnswer(msg);
- } else if (msg.type === 'iceCandidate') {
- onWebRtcIce(msg.candidate);
- } else if (msg.type === 'warning' && msg.warning) {
- console.warn(msg.warning);
- } else if (msg.type === 'peerDataChannels') {
- onWebRtcSFUPeerDatachannels(msg);
- } else {
- console.error("Invalid SS message type", msg.type);
- }
- };
-
- ws.onerror = function (event) {
- console.log(`WS error: ${JSON.stringify(event)}`);
- };
-
- ws.onclose = function (event) {
-
- closeStream();
-
- if (ws.attemptStreamReconnection === true) {
- console.log(`WS closed: ${JSON.stringify(event.code)} - ${event.reason}`);
- if (event.reason !== "") {
- showTextOverlay(`DISCONNECTED: ${event.reason.toUpperCase()}`);
- }
- else {
- showTextOverlay(`DISCONNECTED`);
- }
-
-
- let reclickToStart = setTimeout(function () {
- start(true)
- }, 4000);
- }
-
- ws = undefined;
- };
-}
-
-// Config data received from WebRTC sender via the Cirrus web server
-function onConfig(config) {
- let playerDiv = document.getElementById('player');
- let playerElement = setupWebRtcPlayer(playerDiv, config);
- resizePlayerStyle();
- registerMouse(playerElement);
-}
-
-
-
-
-function registerMouse(playerElement) {
- clearMouseEvents(playerElement);
-
- switch (inputOptions.controlScheme) {
- case ControlSchemeType.HoveringMouse:
- registerHoveringMouseEvents(playerElement);
- break;
- case ControlSchemeType.LockedMouse:
- registerLockedMouseEvents(playerElement);
- break;
- default:
- registerLockedMouseEvents(playerElement);
- break;
- }
-
- let player = document.getElementById("player");
- player.style.cursor = styleCursor;
-}
-
-function clearMouseEvents(playerElement) {
- playerElement.onclick = null;
- playerElement.onmousedown = null;
- playerElement.onmouseup = null;
- playerElement.onwheel = null;
- playerElement.onmousemove = null;
- playerElement.oncontextmenu = null;
-}
-
-function toggleControlScheme() {
- let schemeToggle = document.getElementById("control-scheme-text");
-
- switch (inputOptions.controlScheme) {
- case ControlSchemeType.HoveringMouse:
- inputOptions.controlScheme = ControlSchemeType.LockedMouse;
- schemeToggle.innerHTML = "Control Scheme: Locked Mouse";
- break;
- case ControlSchemeType.LockedMouse:
- inputOptions.controlScheme = ControlSchemeType.HoveringMouse;
- schemeToggle.innerHTML = "Control Scheme: Hovering Mouse";
- break;
- default:
- inputOptions.controlScheme = ControlSchemeType.LockedMouse;
- schemeToggle.innerHTML = "Control Scheme: Locked Mouse";
- console.log(`ERROR: Unknown control scheme ${inputOptions.controlScheme}, defaulting to Locked Mouse`);
- break;
- }
-
- console.log(`Updating control scheme to: ${inputOptions.controlScheme ? "Hovering Mouse" : "Locked Mouse"}`)
- if (webRtcPlayerObj && webRtcPlayerObj.video) {
- registerMouse(webRtcPlayerObj.video);
- }
-}
-
-function toggleBrowserCursorVisibility() {
- inputOptions.hideBrowserCursor = !inputOptions.hideBrowserCursor;
- styleCursor = (inputOptions.hideBrowserCursor ? 'none' : 'default');
- let player = document.getElementById("player");
- player.style.cursor = styleCursor;
-}
-
-function restartStream() {
- if (!ws) {
- return;
- }
- ws.attemptStreamReconnection = false;
-
- let existingOnClose = ws.onclose;
-
- ws.onclose = function (event) {
- existingOnClose(event);
- // this is how we restart
- connect_on_load = true;
- start(false);
- }
-
- // Closing the websocket closes the connection to signalling server, ending the peer connection, and closing the clientside stream too.
- ws.close();
-}
-
-export function closeStream() {
- console.log("----------------------Closing stream----------------------")
- if (webRtcPlayerObj) {
- // Remove video element from the page.
- let playerDiv = document.getElementById('player');
- if (playerDiv) {
- playerDiv.removeChild(webRtcPlayerObj.video);
- }
- // Close the peer connection and associated webrtc machinery.
- webRtcPlayerObj.close();
- webRtcPlayerObj = undefined;
- }
-}
-
-export function load() {
- parseURLParams();
- setupHtmlEvents();
- registerMessageHandlers();
- populateDefaultProtocol();
- setupFreezeFrameOverlay();
- registerKeyboardEvents();
- // Example response event listener that logs to console
- addResponseEventListener('logListener', (response) => { console.log(`Received response message from streamer: "${response}"`) })
- start(false);
-}
\ No newline at end of file
diff --git a/src/store/reducers/sessionSlice.ts b/src/store/reducers/sessionSlice.ts
index bd98047..713dad9 100644
--- a/src/store/reducers/sessionSlice.ts
+++ b/src/store/reducers/sessionSlice.ts
@@ -1,4 +1,3 @@
-import { IData } from "../../models/IData";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { createSession, connectSession } from "./ActionCreator";
@@ -36,7 +35,7 @@ export const sessionSlice = createSlice({
setUserCount(state, action) {
console.log(action.payload)
- const newArr = new Array(action.payload).fill('user')
+ const newArr = new Array(action.payload).fill('user') ///пока нет авторизации и отслеживания пользователей в сессии, нужно как-то отслеживать подключенных пользователей, временное решение для этого.
state.playerCount = newArr
}
},
diff --git a/src/utils/app.js b/src/utils/app.js
index d8df69a..6fab55a 100644
--- a/src/utils/app.js
+++ b/src/utils/app.js
@@ -2882,6 +2882,7 @@ export function closeStream() {
}
// Close the peer connection and associated webrtc machinery.
webRtcPlayerObj.close();
+ ws.close()
webRtcPlayerObj = undefined;
}
}