Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions images/chromium-headful/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ RUN set -eux; \
make -j$(nproc); \
make install;

FROM ghcr.io/onkernel/neko/base:3.0.8-v1.3.0 AS neko
FROM ghcr.io/onkernel/neko/base:3.0.8-v1.3.1 AS neko
# ^--- now has event.SYSTEM_PONG with legacy support to keepalive
FROM node:22-bullseye-slim AS node-22
FROM docker.io/ubuntu:22.04
Expand Down Expand Up @@ -246,4 +246,4 @@ COPY server/runtime/playwright-executor.ts /usr/local/lib/playwright-executor.ts

RUN useradd -m -s /bin/bash kernel

ENTRYPOINT [ "/wrapper.sh" ]
ENTRYPOINT [ "/wrapper.sh" ]
2 changes: 1 addition & 1 deletion images/chromium-headful/client/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ RUN --mount=type=cache,target=/root/.npm npm run build

#
# artifacts from this stage
# COPY --from=client /src/dist/ /var/www
# COPY --from=client /src/dist/ /var/www
31 changes: 31 additions & 0 deletions images/chromium-headful/client/src/neko/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import {
SignalCandidatePayload,
SignalOfferPayload,
SignalAnswerMessage,
BenchmarkWebRTCStatsPayload,
} from './messages'

import { WebRTCStatsCollector } from './webrtc-stats-collector'

export interface BaseEvents {
info: (...message: any[]) => void
warn: (...message: any[]) => void
Expand All @@ -28,6 +31,22 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
protected _state: RTCIceConnectionState = 'disconnected'
protected _id = ''
protected _candidates: RTCIceCandidate[] = []
protected _webrtcStatsCollector: WebRTCStatsCollector

constructor() {
super()

// Initialize WebRTC stats collector
this._webrtcStatsCollector = new WebRTCStatsCollector((stats: BenchmarkWebRTCStatsPayload) => {
// Send stats to server via WebSocket
if (this.connected) {
this._ws!.send(JSON.stringify({
event: EVENT.BENCHMARK.WEBRTC_STATS,
payload: stats,
}))
}
})
}

get id() {
return this._id
Expand Down Expand Up @@ -88,6 +107,9 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
this._ws_heartbeat = undefined
}

// Stop WebRTC stats collection
this._webrtcStatsCollector.stop()

if (this._ws) {
// reset all events
this._ws.onmessage = () => {}
Expand Down Expand Up @@ -241,18 +263,27 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
break
case 'connected':
this.onConnected()
// Start WebRTC stats collection
if (this._peer) {
this._webrtcStatsCollector.start(this._peer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raiden-staging just double checking my understanding--the client would always be collecting these stats and sending them to the server, and then when you hit the /dev/benchmark endpoint it observes the incoming metrics from all connected webrtc clients and computes stats?

this.emit('debug', 'started WebRTC stats collection')
}
break
case 'disconnected':
this[EVENT.RECONNECTING]()
// Stop stats collection on disconnection
this._webrtcStatsCollector.stop()
break
// https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling#ice_connection_state
// We don't watch the disconnected signaling state here as it can indicate temporary issues and may
// go back to a connected state after some time. Watching it would close the video call on any temporary
// network issue.
case 'failed':
this._webrtcStatsCollector.stop()
this.onDisconnected(new Error('peer failed'))
break
case 'closed':
this._webrtcStatsCollector.stop()
this.onDisconnected(new Error('peer closed'))
break
}
Expand Down
6 changes: 6 additions & 0 deletions images/chromium-headful/client/src/neko/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export const EVENT = {
RELEASE: 'admin/release',
GIVE: 'admin/give',
},
BENCHMARK: {
WEBRTC_STATS: 'benchmark/webrtc_stats',
},
} as const

export type Events = typeof EVENT
Expand All @@ -82,6 +85,7 @@ export type WebSocketEvents =
| ScreenEvents
| BroadcastEvents
| AdminEvents
| BenchmarkEvents

export type ControlEvents =
| typeof EVENT.CONTROL.LOCKED
Expand Down Expand Up @@ -122,3 +126,5 @@ export type AdminEvents =
| typeof EVENT.ADMIN.CONTROL
| typeof EVENT.ADMIN.RELEASE
| typeof EVENT.ADMIN.GIVE

export type BenchmarkEvents = typeof EVENT.BENCHMARK.WEBRTC_STATS
59 changes: 59 additions & 0 deletions images/chromium-headful/client/src/neko/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export type WebSocketPayloads =
| BroadcastStatusPayload
| BroadcastCreatePayload
| SystemPongPayload
| BenchmarkWebRTCStatsPayload

export interface WebSocketMessage {
event: WebSocketEvents | string
Expand Down Expand Up @@ -278,3 +279,61 @@ export type AdminLockResource = 'login' | 'control' | 'file_transfer'
export interface AdminLockPayload {
resource: AdminLockResource
}

/*
BENCHMARK PAYLOADS
*/
export interface BenchmarkWebRTCStatsPayload {
timestamp: string
connection_state: string
ice_connection_state: string
frame_rate_fps: {
target: number
achieved: number
min: number
max: number
}
frame_latency_ms: {
p50: number
p95: number
p99: number
}
bitrate_kbps: {
video: number
audio: number
total: number
}
packets: {
video_received: number
video_lost: number
audio_received: number
audio_lost: number
loss_percent: number
}
frames: {
received: number
dropped: number
decoded: number
corrupted: number
key_frames_decoded: number
}
jitter_ms: {
video: number
audio: number
}
network: {
rtt_ms: number
available_outgoing_bitrate_kbps: number
bytes_received: number
bytes_sent: number
}
codecs: {
video: string
audio: string
}
resolution: {
width: number
height: number
}
concurrent_viewers: number
}
Loading
Loading