123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944 |
- '5.1.3';
- const SpecialKeyCodes = {
- Backspace: 8,
- ShiftLeft: 16,
- ControlLeft: 17,
- AltLeft: 18,
- ShiftRight: 253,
- ControlRight: 254,
- AltRight: 255,
- };
- const MouseButton = {
- MainButton: 0,
- AuxiliaryButton: 1,
- SecondaryButton: 2,
- FourthButton: 3,
- FifthButton: 4,
- };
- const MouseButtonsMask = {
- 1: 0,
- 2: 2,
- 4: 1,
- 8: 3,
- 16: 4,
- };
- const RECEIVE = {
- QualityControlOwnership: 0,
- Response: 1,
- Command: 2,
- FreezeFrame: 3,
- UnfreezeFrame: 4,
- VideoEncoderAvgQP: 5,
- LatencyTest: 6,
- InitialSettings: 7,
- FileExtension: 8,
- FileMimeType: 9,
- FileContents: 10,
- InputControlOwnership: 12,
- CompositionStart: 64,
- Protocol: 255,
- };
- const SEND = {
-
- IFrameRequest: 0,
- RequestQualityControl: 1,
- FpsRequest: 2,
- AverageBitrateRequest: 3,
- StartStreaming: 4,
- StopStreaming: 5,
- LatencyTest: 6,
- RequestInitialSettings: 7,
-
-
- UIInteraction: 50,
- Command: 51,
-
- KeyDown: 60,
- KeyUp: 61,
- KeyPress: 62,
- FindFocus: 63,
- CompositionEnd: 64,
-
- MouseEnter: 70,
- MouseLeave: 71,
- MouseDown: 72,
- MouseUp: 73,
- MouseMove: 74,
- MouseWheel: 75,
-
- TouchStart: 80,
- TouchEnd: 81,
- TouchMove: 82,
-
- GamepadButtonPressed: 90,
- GamepadButtonReleased: 91,
- GamepadAnalog: 92,
- };
- let iceServers = undefined;
- class PeerStream extends HTMLVideoElement {
- constructor() {
- super();
- window.ps = this;
- this.ws = {
- send() {},
- close() {},
- };
- this.pc = {
- close() {},
- };
- this.setupVideo();
- this.registerKeyboardEvents();
- this.registerMouseHoverEvents();
- this.registerFakeMouseEvents();
-
-
- this.sceneId = this.dataset.sceneId;
- this.viewMode = this.dataset.viewMode;
- this.token = this.dataset.token;
-
- this._onClose = null;
- this._onOpen = null;
- this._onMessage = null;
- document.addEventListener(
- 'pointerlockchange',
- () => {
- if (document.pointerLockElement === this) {
- this.registerPointerLockEvents();
- } else {
- this.registerMouseHoverEvents();
- }
- },
- false,
- );
- this.addEventListener('loadeddata', (e) => {
- this.style['aspect-ratio'] = this.videoWidth / this.videoHeight;
- });
-
- }
- set onclose(callback) {
- if (typeof callback === 'function') {
- this._onClose = callback;
- }
- }
- set onopen(callback) {
- if (typeof callback === 'function') {
- this._onOpen = callback;
- }
- }
- set onmessage(callback) {
- if (typeof callback === 'function') {
- this._onMessage = callback;
- }
- }
- checkWebRTCSupport() {
-
- const RTCPeerConnection =
- window.RTCPeerConnection ||
- window.webkitRTCPeerConnection ||
- window.mozRTCPeerConnection;
- if (!RTCPeerConnection) {
- console.warn('checkWebRTCSupport RTCPeerConnection not supported');
- return false;
- }
-
- let dataChannelSupported = false;
- let pc = null;
- if (RTCPeerConnection) {
- try {
- pc = new RTCPeerConnection();
- const dc = pc.createDataChannel('test');
- dataChannelSupported = !!dc;
- dc.close();
- pc.close();
- } catch (e) {
- console.error(e);
- console.warn('checkWebRTCSupport dataChannelSupported not supported');
- return false;
- }
- if (!dataChannelSupported) {
- console.warn('checkWebRTCSupport DataChannel not supported');
- return false;
- }
- }
- return true;
- }
-
- async connectedCallback() {
- if (false == this.checkWebRTCSupport()) {
- const overlayDiv = document.createElement('div');
- overlayDiv.innerHTML =
- '你的浏览器版本过低!<br>推荐使用谷歌100以上版本浏览器!!';
- overlayDiv.style.position = 'absolute';
- overlayDiv.style.top = '50%';
- overlayDiv.style.left = '50%';
- overlayDiv.style.transform = 'translate(-50%, -50%)';
- overlayDiv.style.background = 'rgba(255, 255, 255, 0.8)';
- overlayDiv.style.padding = '10px';
- overlayDiv.style.borderRadius = '5px';
- overlayDiv.style.display = 'block';
- this.parentNode.appendChild(overlayDiv);
- }
-
- if (!this.isConnected) return;
- if (
- this.pc.connectionState === 'connected' &&
- this.dc.readyState === 'open' &&
- this.ws.readyState === 1
- ) {
-
- this.play();
- return;
- }
-
- this.ws.onclose = null;
- this.ws.close(1000);
- this.sceneId = this.dataset.sceneId;
- this.viewMode = this.dataset.viewMode;
- this.token = this.dataset.token;
- const wsUrl =
-
-
- 'ws:10.1.162.193:3000' +
- `?sceneId=${this.sceneId}&token=${this.token}&&view_mode=${this.viewMode}`;
- this.ws = new WebSocket(wsUrl, 'peer-stream');
- this.ws.onerror;
- this.ws.onopen = () => {
- console.info('✅', this.ws);
- };
- this.ws.onmessage = (e) => {
- this.onWebSocketMessage(e.data);
- };
- this.ws.onclose = (e) => {
- console.warn(e);
- this.dispatchEvent(new CustomEvent('playerdisconnected', {}));
- this.dispatchEvent(new CustomEvent('disConnected', {}));
-
-
- };
- }
-
- static get observedAttributes() {
- return ['sceneId', 'token','viewMode'];
- }
- disconnectedCallback() {
- if(this._onClose)this._onClose();
-
- setTimeout(() => {
- if (this.isConnected) return;
- this.ws.close(1000);
- this.pc.close();
- console.log('❌ peer connection closing');
- if(this.dc)this.dc.close();
- }, 100);
- }
- adoptedCallback() {}
- attributeChangedCallback(name, oldValue, newValue) {
- if (!this.isConnected) return;
-
- this.ws.close(1000);
-
- if (name === 'viewMode') {
- this.viewMode = newValue;
- } else if (name === 'sceneId') {
- this.sceneId = newValue ;
- } else if(name === 'token') {
- this.token = newValue;
- }
- }
- async onWebSocketMessage(msg) {
- try {
- msg = JSON.parse(msg);
- } catch {
- console.debug('↓↓', msg);
- return;
- }
- if (msg.type === 'offer') {
- this.setupPeerConnection();
- const offer = new RTCSessionDescription(msg);
- console.log('↓↓ offer', offer);
- await this.pc.setRemoteDescription(offer);
-
- this.pc.addTransceiver('video', { direction: 'recvonly' });
- const answer = await this.pc.createAnswer();
- await this.pc.setLocalDescription(answer);
- console.log('↑↑ answer', answer);
- this.ws.send(JSON.stringify(answer));
- for (let receiver of this.pc.getReceivers()) {
- receiver.playoutDelayHint = 0;
- }
- } else if (msg.type === 'iceCandidate') {
- const candidate = new RTCIceCandidate(msg.candidate);
- console.log('↓↓ candidate:', candidate);
- await this.pc.addIceCandidate(candidate);
- } else if (msg.type === 'answer') {
- const answer = new RTCSessionDescription(msg);
- await this.pc.setRemoteDescription(answer);
- console.log('↓↓ answer:', answer);
- for (const receiver of this.pc.getReceivers()) {
- receiver.playoutDelayHint = 0;
- }
- } else if (msg.type === 'playerqueue') {
- this.dispatchEvent(new CustomEvent('playerqueue', { detail: msg }));
- console.log('↓↓ playerqueue:', msg);
- } else if (msg.type === 'setIceServers') {
- iceServers = msg.iceServers;
- console.log('↓↓ setIceServers:', msg);
- } else if (msg.type === 'playerConnected') {
- console.log('↓↓ playerConnected:', msg);
- this.setupPeerConnection_ue4();
- this.setupDataChannel_ue4();
- } else if (msg.type === 'ping') {
- console.log('↓↓ ping:', msg);
- msg.type = 'pong';
- this.ws.send(JSON.stringify(msg));
- if (this.mouseReleaseTime) {
- let now = new Date();
- if (now - this.lastmouseTime > this.mouseReleaseTime * 1000) {
- msg.type = 'mouseRelease';
- this.ws.send(JSON.stringify(msg));
- }
- }
- } else if (msg.type === 'ueDisConnected') {
- this.dispatchEvent(new CustomEvent('ueDisConnected', { detail: msg }));
- console.log('↓↓ ueDisConnected:', msg);
- } else if (msg.type === 'setmouseReleaseTime') {
- this.mouseReleaseTime = msg.mouseReleaseTime;
- this.lastmouseTime = new Date();
- console.log('↓↓ setmouseReleaseTime:', msg);
- } else if (msg.type === 'getStatus') {
- console.log('↓↓ getStatus:', msg);
- this.handleGetStatus(msg);
- } else {
- console.warn('↓↓', msg);
- this.dispatchEvent(new CustomEvent('message', { detail:JSON.stringify(msg) }));
- }
- }
- handleGetStatus(msg) {
- if (false == this.pc instanceof RTCPeerConnection) {
- msg.videoencoderqp = null;
- msg.netrate = null;
- this.ws.send(JSON.stringify(msg));
- console.log('↑↑ handleGetStatus:', msg);
- return;
- }
- let initialBytesReceived = 0;
-
- this.pc.getStats(null).then((stats) => {
- stats.forEach((report) => {
- if (report.type === 'transport') {
- initialBytesReceived = report.bytesReceived;
- }
- });
- });
-
- let durationInSeconds = 0.2;
- setTimeout(() => {
- this.pc.getStats(null).then((stats) => {
- stats.forEach((report) => {
- if (report.type === 'transport') {
- const finalBytesReceived = report.bytesReceived;
- const bytesReceived = finalBytesReceived - initialBytesReceived;
-
- const averageReceiveBandwidth =
- ((bytesReceived / durationInSeconds) * 8) / 1000 / 1000;
- msg.videoencoderqp = this.VideoEncoderQP;
- msg.netrate = averageReceiveBandwidth.toFixed(2);
- this.ws.send(JSON.stringify(msg));
- console.log('↑↑ handleGetStatus:', msg);
- }
- });
- });
- }, durationInSeconds * 1000);
- }
- onDataChannelMessage(data) {
- data = new Uint8Array(data);
- const utf16 = new TextDecoder('utf-16');
- switch (data[0]) {
- case RECEIVE.VideoEncoderAvgQP: {
- this.VideoEncoderQP = +utf16.decode(data.slice(1));
-
- break;
- }
- case RECEIVE.Response: {
-
- const detail = utf16.decode(data.slice(1));
- this.dispatchEvent(new CustomEvent('message', { detail }));
- console.info(detail);
- break;
- }
- case RECEIVE.Command: {
- const command = JSON.parse(utf16.decode(data.slice(1)));
- console.info('↓↓ command:', command);
- if (command.command === 'onScreenKeyboard') {
- console.info('You should setup a on-screen keyboard');
- if (command.showOnScreenKeyboard) {
- if (this.enableChinese) {
- let input = document.createElement('input');
- input.style.position = 'fixed';
- input.style.zIndex = -1;
- input.autofocus = true;
- document.body.append(input);
- input.focus();
- input.addEventListener('compositionend', (e) => {
- console.log(e.data);
- this.emitMessage(e.data, SEND.CompositionEnd);
- });
- input.addEventListener('blue', (e) => {
- input.remove();
- });
- input.addEventListener('keydown', (e) => {
- this.onkeydown(e);
- });
- input.addEventListener('keyup', (e) => {
- this.onkeyup(e);
- });
- input.addEventListener('keypress', (e) => {
- this.onkeypress(e);
- });
- }
- }
- }
- break;
- }
- case RECEIVE.FreezeFrame: {
- const size = new DataView(data.slice(1, 5).buffer).getInt32(0, true);
- const jpeg = data.slice(1 + 4);
- console.info('↓↓ freezed frame:', jpeg);
- break;
- }
- case RECEIVE.UnfreezeFrame: {
- console.info('↓↓ 【unfreeze frame】');
- break;
- }
- case RECEIVE.LatencyTest: {
- const latencyTimings = JSON.parse(utf16.decode(data.slice(1)));
- console.info('↓↓ latency timings:', latencyTimings);
- break;
- }
- case RECEIVE.QualityControlOwnership: {
- this.QualityControlOwnership = data[1] !== 0;
- console.info(
- '↓↓ Quality Control Ownership:',
- this.QualityControlOwnership,
- );
- break;
- }
- case RECEIVE.InitialSettings: {
- this.InitialSettings = JSON.parse(utf16.decode(data.slice(1)));
- console.log('↓↓ initial setting:', this.InitialSettings);
- break;
- }
- case RECEIVE.InputControlOwnership: {
- this.InputControlOwnership = data[1] !== 0;
- console.log('↓↓ input control ownership:', this.InputControlOwnership);
- break;
- }
- case RECEIVE.Protocol: {
- let protocol = JSON.parse(utf16.decode(data.slice(1)));
- console.log(protocol);
- if (protocol.Direction === 0) {
- for (let key in protocol) {
- SEND[key] = protocol[key].id;
- }
- } else if (protocol.Direction === 1) {
- for (let key in protocol) {
- RECEIVE[key] = protocol[key].id;
- }
- }
- this.dc.send(new Uint8Array([SEND.RequestInitialSettings]));
- this.dc.send(new Uint8Array([SEND.RequestQualityControl]));
- break;
- }
- default: {
- console.error('↓↓ invalid data:', data);
- }
- }
- }
- setupVideo() {
- this.tabIndex = 0;
-
- this.playsInline = true;
- this.disablepictureinpicture = true;
-
- this.muted = true;
- this.autoplay = true;
-
-
-
- this.style['pointer-events'] = 'none';
- this.style['object-fit'] = 'fill';
- }
- setupDataChannel(e) {
-
-
- this.dc = e.channel;
-
- this.dc.binaryType = 'arraybuffer';
- this.dc.onopen = (e) => {
- console.log('✅', this.dc);
- this.style.pointerEvents = 'auto';
-
-
-
-
- };
- this.dc.onclose = (e) => {
- console.info('❌ data channel closed');
- this.style.pointerEvents = 'none';
- this.blur();
-
- };
- this.dc.onerror;
- this.dc.onmessage = (e) => {
- this.onDataChannelMessage(e.data);
- };
- }
- setupDataChannel_ue4(label = 'hello') {
-
- this.dc = this.pc.createDataChannel(label, { ordered: true });
-
- this.dc.binaryType = 'arraybuffer';
- this.dc.onopen = (e) => {
- console.log('✅ data channel connected:', label);
- this.style.pointerEvents = 'auto';
- this.dc.send(new Uint8Array([SEND.RequestInitialSettings]));
- this.dc.send(new Uint8Array([SEND.RequestQualityControl]));
- };
- this.dc.onclose = (e) => {
- console.info('❌ data channel closed:', label);
- this.style.pointerEvents = 'none';
- };
- this.dc.onmessage = (e) => {
- this.onDataChannelMessage(e.data);
- };
- }
- setupPeerConnection() {
- this.pc.close();
- this.pc = new RTCPeerConnection({
- sdpSemantics: 'unified-plan',
- bundlePolicy: 'balanced',
- iceServers: iceServers,
-
- iceConnectionTimeout: 40000
- });
- this.pc.ontrack = (e) => {
- console.log(`↓↓ ${e.track.kind} track:`, e);
- if (e.track.kind === 'video') {
- this.srcObject = e.streams[0];
- } else if (e.track.kind === 'audio') {
- this.audio = document.createElement('audio');
- this.audio.autoplay = true;
- this.audio.srcObject = e.streams[0];
- }
- };
- this.pc.onicecandidate = (e) => {
-
- if (e.candidate?.candidate) {
- console.log('↑↑ candidate:', e.candidate);
- this.ws.send(
- JSON.stringify({ type: 'iceCandidate', candidate: e.candidate }),
- );
- } else {
-
- }
- };
- this.pc.ondatachannel = (e) => {
- this.setupDataChannel(e);
- };
-
- this.pc.addEventListener('iceconnectionstatechange', () => {
- const state = this.pc.iceConnectionState;
- console.log('ICE connection state:', state);
- if (state === 'connected' || state === 'completed') {
- console.log('WebRTC connection established successfully!');
-
- } else if (state === 'failed') {
- console.error('ICE connection failed!');
- } else if (state === 'disconnected') {
- console.warn('ICE connection disconnected!');
- }
- });
- }
-
- setupPeerConnection_ue4() {
- this.pc.close();
- this.pc = new RTCPeerConnection({
- sdpSemantics: 'unified-plan',
- bundlePolicy: 'balanced',
- iceServers: iceServers,
- });
- this.pc.ontrack = (e) => {
- console.log(`↓↓ ${e.track.kind} track:`, e);
- if (e.track.kind === 'video') {
- this.srcObject = e.streams[0];
- } else if (e.track.kind === 'audio') {
- this.audio = document.createElement('audio');
- this.audio.autoplay = true;
- this.audio.srcObject = e.streams[0];
- }
- };
- this.pc.onicecandidate = (e) => {
-
- if (e.candidate?.candidate) {
- console.log('↑↑ candidate:', e.candidate);
- this.ws.send(
- JSON.stringify({ type: 'iceCandidate', candidate: e.candidate }),
- );
- } else {
-
- }
- };
- this.pc.onnegotiationneeded = (e) => {
- this.setupOffer();
- };
- }
- async setupOffer() {
-
- const offer = await this.pc.createOffer({
- offerToReceiveAudio: +this.hasAttribute('audio'),
- offerToReceiveVideo: 1,
- voiceActivityDetection: false,
- });
-
- offer.sdp = offer.sdp.replace(
- 'useinbandfec=1',
- 'useinbandfec=1;stereo=1;sprop-maxcapturerate=48000',
- );
- this.pc.setLocalDescription(offer);
- this.ws.send(JSON.stringify(offer));
- console.log('↓↓ sending offer:', offer);
- }
- keysDown = new Set();
- registerKeyboardEvents() {
- this.onkeydown = (e) => {
- const keyCode = SpecialKeyCodes[e.code] || e.keyCode;
- this.dc.send(new Uint8Array([SEND.KeyDown, keyCode, e.repeat]));
- this.keysDown.add(keyCode);
-
-
- if (e.keyCode === SpecialKeyCodes.Backspace) {
- this.onkeypress({
- keyCode: SpecialKeyCodes.Backspace,
- });
- }
-
-
- };
- this.onkeyup = (e) => {
- const keyCode = SpecialKeyCodes[e.code] || e.keyCode;
- this.dc.send(new Uint8Array([SEND.KeyUp, keyCode]));
- this.keysDown.delete(keyCode);
- };
- this.onkeypress = (e) => {
- const data = new DataView(new ArrayBuffer(3));
- data.setUint8(0, SEND.KeyPress);
- data.setUint16(1, SpecialKeyCodes[e.code] || e.keyCode, true);
- this.dc.send(data);
- };
- this.onblur = (e) => {
- this.keysDown.forEach((keyCode) => {
- this.dc.send(new Uint8Array([SEND.KeyUp, keyCode]));
- });
- this.keysDown.clear();
- };
- }
- registerTouchEvents() {
-
-
- const fingers = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
- const fingerIds = {};
- this.ontouchstart = (e) => {
-
- for (const touch of e.changedTouches) {
-
- const finger = fingers.pop();
- if (finger === undefined) {
- console.info('exhausted touch indentifiers');
- }
- fingerIds[touch.identifier] = finger;
- }
- this.emitTouchData(SEND.TouchStart, e.changedTouches, fingerIds);
- e.preventDefault();
- };
- this.ontouchend = (e) => {
- this.emitTouchData(SEND.TouchEnd, e.changedTouches, fingerIds);
-
- for (const touch of e.changedTouches) {
-
- fingers.push(fingerIds[touch.identifier]);
- delete fingerIds[touch.identifier];
- }
- e.preventDefault();
- };
- this.ontouchmove = (e) => {
- this.emitTouchData(SEND.TouchMove, e.touches, fingerIds);
- e.preventDefault();
- };
- }
-
- registerFakeMouseEvents() {
- let finger = undefined;
- const { left, top } = this.getBoundingClientRect();
- this.ontouchstart = (e) => {
- if (finger === undefined) {
- const firstTouch = e.changedTouches[0];
- finger = {
- id: firstTouch.identifier,
- x: firstTouch.clientX - left,
- y: firstTouch.clientY - top,
- };
-
- this.onmouseenter(e);
- this.emitMouseDown(MouseButton.MainButton, finger.x, finger.y);
- }
- e.preventDefault();
- };
- this.ontouchend = (e) => {
-
- if (finger) {
- for (const touch of e.changedTouches) {
- if (touch.identifier === finger.id) {
- const x = touch.clientX - left;
- const y = touch.clientY - top;
- this.emitMouseUp(MouseButton.MainButton, x, y);
-
- this.onmouseleave(e);
- finger = undefined;
- break;
- }
- }
- }
- e.preventDefault();
- };
- this.ontouchmove = (e) => {
-
- if (finger) {
- for (const touch of e.touches) {
- if (touch.identifier === finger.id) {
- const x = touch.clientX - left;
- const y = touch.clientY - top;
- this.emitMouseMove(x, y, x - finger.x, y - finger.y);
- finger.x = x;
- finger.y = y;
- break;
- }
- }
- }
- e.preventDefault();
- };
- }
- registerMouseHoverEvents() {
- this.registerMouseEnterAndLeaveEvents();
- this.onmousemove = (e) => {
- this.emitMouseMove(e.offsetX, e.offsetY, e.movementX, e.movementY);
- e.preventDefault();
- };
- this.onmousedown = (e) => {
- this.emitMouseDown(e.button, e.offsetX, e.offsetY);
-
- };
- this.onmouseup = (e) => {
- this.emitMouseUp(e.button, e.offsetX, e.offsetY);
-
- };
-
-
- this.oncontextmenu = (e) => {
- this.emitMouseUp(e.button, e.offsetX, e.offsetY);
- e.preventDefault();
- };
- this.onwheel = (e) => {
- this.emitMouseWheel(e.wheelDelta, e.offsetX, e.offsetY);
- e.preventDefault();
- };
- }
- registerPointerLockEvents() {
- this.registerMouseEnterAndLeaveEvents();
- console.info('mouse locked in, ESC to exit');
- const { clientWidth, clientHeight } = this;
- let x = clientWidth / 2;
- let y = clientHeight / 2;
- this.onmousemove = (e) => {
- x += e.movementX;
- y += e.movementY;
- x = (x + clientWidth) % clientWidth;
- y = (y + clientHeight) % clientHeight;
- this.emitMouseMove(x, y, e.movementX, e.movementY);
- };
- this.onmousedown = (e) => {
- this.emitMouseDown(e.button, x, y);
- };
- this.onmouseup = (e) => {
- this.emitMouseUp(e.button, x, y);
- };
- this.onwheel = (e) => {
- this.emitMouseWheel(e.wheelDelta, x, y);
- };
- }
- registerMouseEnterAndLeaveEvents() {
- this.onmouseenter = (e) => {
- this.dc.send(new Uint8Array([SEND.MouseEnter]));
- };
- this.onmouseleave = (e) => {
- if (this.dc.readyState === 'open')
- this.dc.send(new Uint8Array([SEND.MouseLeave]));
-
- for (let i = 1; i <= 16; i *= 2) {
- if (e.buttons & i) {
- this.emitMouseUp(MouseButtonsMask[i], 0, 0);
- }
- }
- };
- }
- emitMouseMove(x, y, deltaX, deltaY) {
- const coord = this.normalize(x, y);
- deltaX = (deltaX * 65536) / this.clientWidth;
- deltaY = (deltaY * 65536) / this.clientHeight;
- const data = new DataView(new ArrayBuffer(9));
- data.setUint8(0, SEND.MouseMove);
- data.setUint16(1, coord.x, true);
- data.setUint16(3, coord.y, true);
- data.setInt16(5, deltaX, true);
- data.setInt16(7, deltaY, true);
- this.dc.send(data);
- this.lastmouseTime = new Date();
- }
- emitMouseDown(button, x, y) {
- const coord = this.normalize(x, y);
- const data = new DataView(new ArrayBuffer(6));
- data.setUint8(0, SEND.MouseDown);
- data.setUint8(1, button);
- data.setUint16(2, coord.x, true);
- data.setUint16(4, coord.y, true);
- this.dc.send(data);
- if (this.enableChinese) {
- this.dc.send(new Uint8Array([SEND.FindFocus]));
- }
- }
- emitMouseUp(button, x, y) {
- const coord = this.normalize(x, y);
- const data = new DataView(new ArrayBuffer(6));
- data.setUint8(0, SEND.MouseUp);
- data.setUint8(1, button);
- data.setUint16(2, coord.x, true);
- data.setUint16(4, coord.y, true);
- this.dc.send(data);
- }
- emitMouseWheel(delta, x, y) {
- const coord = this.normalize(x, y);
- const data = new DataView(new ArrayBuffer(7));
- data.setUint8(0, SEND.MouseWheel);
- data.setInt16(1, delta, true);
- data.setUint16(3, coord.x, true);
- data.setUint16(5, coord.y, true);
- this.dc.send(data);
- }
- emitTouchData(type, touches, fingerIds) {
- const data = new DataView(new ArrayBuffer(2 + 6 * touches.length));
- data.setUint8(0, type);
- data.setUint8(1, touches.length);
- let byte = 2;
- for (const touch of touches) {
- const x = touch.clientX - this.offsetLeft;
- const y = touch.clientY - this.offsetTop;
- const coord = this.normalize(x, y);
- data.setUint16(byte, coord.x, true);
- byte += 2;
- data.setUint16(byte, coord.y, true);
- byte += 2;
- data.setUint8(byte, fingerIds[touch.identifier], true);
- byte += 1;
- data.setUint8(byte, 255 * touch.force, true);
- byte += 1;
- }
- this.dc.send(data);
- }
-
- emitMessage(msg, messageType = SEND.UIInteraction) {
- if (typeof msg !== 'string') msg = JSON.stringify(msg);
-
- const data = new DataView(new ArrayBuffer(1 + 2 + 2 * msg.length));
- let byteIdx = 0;
- data.setUint8(byteIdx, messageType);
- byteIdx++;
- data.setUint16(byteIdx, msg.length, true);
- byteIdx += 2;
- for (let i = 0; i < msg.length; i++) {
-
- data.setUint16(byteIdx, msg.charCodeAt(i), true);
- byteIdx += 2;
- }
- this.dc.send(data);
- return new Promise((resolve) =>
- this.addEventListener('message', (e) => resolve(e.detail), {
- once: true,
- }),
- );
- }
- normalize(x, y) {
- const normalizedX = x / this.clientWidth;
- const normalizedY = y / this.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,
- };
- }
- }
- }
- customElements.define('peer-stream', PeerStream, { extends: 'video' });
|