From a22c48c4ca34e245a67d3c19deb4b4ea3ef8a802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Fri, 20 Jan 2023 23:09:01 +0100 Subject: [PATCH] WebRTC latency in stats (#20) * webrtc stats full report. * add ping+pong latency counter. * single console log. * remove report & add latency to webrtc stats. * fix. --- src/component/internal/webrtc.ts | 32 ++++++++++++++++++++++++++++++++ src/component/types/webrtc.ts | 3 +++ src/page/components/events.vue | 14 ++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/src/component/internal/webrtc.ts b/src/component/internal/webrtc.ts index 1a0cec9c..cdd1522f 100644 --- a/src/component/internal/webrtc.ts +++ b/src/component/internal/webrtc.ts @@ -3,6 +3,8 @@ import { WebRTCStats, CursorPosition, CursorImage } from '../types/webrtc' import { Logger } from '../utils/logger' import { videoSnap } from '../utils/video-snap' +const maxUint32 = 2 ** 32 - 1 + export const OPCODE = { MOVE: 0x01, SCROLL: 0x02, @@ -10,6 +12,7 @@ export const OPCODE = { KEY_UP: 0x04, BTN_DOWN: 0x05, BTN_UP: 0x06, + PING: 0x07, } as const export interface ICEServer { @@ -44,6 +47,8 @@ export class NekoWebRTC extends EventEmitter { private _connected = false private _candidates: RTCIceCandidateInit[] = [] private _statsStop?: () => void + private _requestLatency = 0 + private _responseLatency = 0 // eslint-disable-next-line constructor( @@ -340,6 +345,7 @@ export class NekoWebRTC extends EventEmitter { public send(event: 'wheel' | 'mousemove', data: { x: number; y: number }): void public send(event: 'mousedown' | 'mouseup' | 'keydown' | 'keyup', data: { key: number }): void + public send(event: 'ping', data: number): void public send(event: string, data: any): void { if (typeof this._channel === 'undefined' || this._channel.readyState !== 'open') { this._log.warn(`attempting to send data, but data-channel is not open`, { event }) @@ -393,6 +399,14 @@ export class NekoWebRTC extends EventEmitter { payload.setUint16(1, 4) payload.setUint32(3, data.key) break + case 'ping': + buffer = new ArrayBuffer(11) + payload = new DataView(buffer) + payload.setUint8(0, OPCODE.PING) + payload.setUint16(1, 8) + payload.setUint32(3, Math.trunc(data / maxUint32)) + payload.setUint32(7, data % maxUint32) + break default: this._log.warn(`unknown data event`, { event }) return @@ -457,6 +471,18 @@ export class NekoWebRTC extends EventEmitter { } reader.readAsDataURL(blob) + break + case 3: + const nowTs = Date.now() + + const [clientTs1, clientTs2] = [payload.getUint32(3), payload.getUint32(7)] + const clientTs = clientTs1 * maxUint32 + clientTs2 + const [serverTs1, serverTs2] = [payload.getUint32(11), payload.getUint32(15)] + const serverTs = serverTs1 * maxUint32 + serverTs2 + + this._requestLatency = serverTs - clientTs + this._responseLatency = nowTs - serverTs + break default: this._log.warn(`unhandled webrtc event`, { event, payload }) @@ -534,6 +560,10 @@ export class NekoWebRTC extends EventEmitter { width: report.frameWidth || NaN, height: report.frameHeight || NaN, muted: this._track?.muted, + // latency from ping/pong messages + latency: this._requestLatency + this._responseLatency, + requestLatency: this._requestLatency, + responseLatency: this._responseLatency, }) } @@ -542,6 +572,8 @@ export class NekoWebRTC extends EventEmitter { framesDecoded = report.framesDecoded packetsLost = report.packetsLost packetsReceived = report.packetsReceived + + this.send('ping', Date.now()) }, ms) return function () { diff --git a/src/component/types/webrtc.ts b/src/component/types/webrtc.ts index bedc243c..7059b86c 100644 --- a/src/component/types/webrtc.ts +++ b/src/component/types/webrtc.ts @@ -6,6 +6,9 @@ export interface WebRTCStats { width: number height: number muted?: boolean + latency: number + requestLatency: number + responseLatency: number } export interface CursorPosition { diff --git a/src/page/components/events.vue b/src/page/components/events.vue index 0733dd7d..6d75abb4 100644 --- a/src/page/components/events.vue +++ b/src/page/components/events.vue @@ -109,6 +109,20 @@ bitrate {{ Math.floor(neko.state.connection.webrtc.stats.bitrate / 1024 / 8) }} KB/s + + latency + + {{ neko.state.connection.webrtc.stats.latency }}ms + + loss