From 79d88d40feab366ef7ac52096d126ff6b96610a0 Mon Sep 17 00:00:00 2001 From: lukasIO Date: Tue, 19 May 2026 17:27:29 +0200 Subject: [PATCH 1/4] add method to query rtcStats on room --- packages/livekit-rtc/src/room.ts | 34 ++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/livekit-rtc/src/room.ts b/packages/livekit-rtc/src/room.ts index b0a89dd2..0f237d2d 100644 --- a/packages/livekit-rtc/src/room.ts +++ b/packages/livekit-rtc/src/room.ts @@ -2,8 +2,12 @@ // // SPDX-License-Identifier: Apache-2.0 import { Mutex } from '@livekit/mutex'; -import { EncryptionState, type EncryptionType } from '@livekit/rtc-ffi-bindings'; -import type { FfiEvent } from '@livekit/rtc-ffi-bindings'; +import { + EncryptionState, + type EncryptionType, + GetSessionStatsRequest, +} from '@livekit/rtc-ffi-bindings'; +import type { FfiEvent, GetSessionStatsCallback } from '@livekit/rtc-ffi-bindings'; import { DisconnectReason, type OwnedParticipant } from '@livekit/rtc-ffi-bindings'; import type { DataStream_Trailer, @@ -191,6 +195,32 @@ export class Room extends (EventEmitter as new () => TypedEmitter return this.sidPromise; } + async getRtcStats() { + if (!this.isConnected || !this.ffiHandle) { + throw new Error('getRtcStats requires a connected room'); + } + const res = FfiClient.instance.request({ + message: { + case: 'getSessionStats', + value: { + roomHandle: this.ffiHandle.handle, + }, + }, + }); + const cb = await FfiClient.instance.waitFor( + (ev: FfiEvent) => + ev.message.case === 'getSessionStats' && ev.message.value.asyncId === res.requestAsyncId, + { signal: this.disconnectController.signal }, + ); + if (cb.message.case === 'error') { + throw new Error(cb.message.value); + } else if (cb.message.case === 'result') { + return cb.message.value; + } else { + throw new Error('could not retrieve rtc stats'); + } + } + get numParticipants(): number { return this.info?.numParticipants ?? 0; } From fa8540888ba30a5a7028da276a6f3e00bf62cdc0 Mon Sep 17 00:00:00 2001 From: lukasIO Date: Wed, 20 May 2026 14:29:39 +0200 Subject: [PATCH 2/4] fix ffi request logic --- packages/livekit-rtc/src/ffi_client.ts | 6 ++++++ packages/livekit-rtc/src/room.ts | 23 ++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/livekit-rtc/src/ffi_client.ts b/packages/livekit-rtc/src/ffi_client.ts index 9fbd7d91..f81ac8d8 100644 --- a/packages/livekit-rtc/src/ffi_client.ts +++ b/packages/livekit-rtc/src/ffi_client.ts @@ -41,6 +41,8 @@ export class FfiClient extends (EventEmitter as new () => TypedEmitter TypedEmitter( predicate: (ev: FfiEvent) => boolean, options?: { signal?: AbortSignal }, diff --git a/packages/livekit-rtc/src/room.ts b/packages/livekit-rtc/src/room.ts index 0f237d2d..d5b2bb45 100644 --- a/packages/livekit-rtc/src/room.ts +++ b/packages/livekit-rtc/src/room.ts @@ -7,7 +7,11 @@ import { type EncryptionType, GetSessionStatsRequest, } from '@livekit/rtc-ffi-bindings'; -import type { FfiEvent, GetSessionStatsCallback } from '@livekit/rtc-ffi-bindings'; +import type { + FfiEvent, + GetSessionStatsCallback, + GetSessionStatsResponse, +} from '@livekit/rtc-ffi-bindings'; import { DisconnectReason, type OwnedParticipant } from '@livekit/rtc-ffi-bindings'; import type { DataStream_Trailer, @@ -35,6 +39,7 @@ import { } from '@livekit/rtc-ffi-bindings'; import { TrackKind } from '@livekit/rtc-ffi-bindings'; import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter'; +import { randomInt } from 'crypto'; import EventEmitter from 'events'; import { ByteStreamReader, TextStreamReader } from './data_streams/stream_reader.js'; import type { @@ -199,19 +204,23 @@ export class Room extends (EventEmitter as new () => TypedEmitter if (!this.isConnected || !this.ffiHandle) { throw new Error('getRtcStats requires a connected room'); } - const res = FfiClient.instance.request({ + // subscribe before issuing the request + const requestId = FfiClient.instance.getNextRequestAsyncId(); + const cbPromise = FfiClient.instance.waitFor( + (ev: FfiEvent) => + ev.message.case === 'getSessionStats' && ev.message.value.asyncId === requestId, + { signal: this.disconnectController.signal }, + ); + FfiClient.instance.request({ message: { case: 'getSessionStats', value: { roomHandle: this.ffiHandle.handle, + requestAsyncId: requestId, }, }, }); - const cb = await FfiClient.instance.waitFor( - (ev: FfiEvent) => - ev.message.case === 'getSessionStats' && ev.message.value.asyncId === res.requestAsyncId, - { signal: this.disconnectController.signal }, - ); + const cb = await cbPromise; if (cb.message.case === 'error') { throw new Error(cb.message.value); } else if (cb.message.case === 'result') { From a08af11c51b460163291548542420a9debf50808 Mon Sep 17 00:00:00 2001 From: lukasIO Date: Wed, 20 May 2026 14:30:28 +0200 Subject: [PATCH 3/4] Create selfish-numbers-cheat.md --- .changeset/selfish-numbers-cheat.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/selfish-numbers-cheat.md diff --git a/.changeset/selfish-numbers-cheat.md b/.changeset/selfish-numbers-cheat.md new file mode 100644 index 00000000..e1b7bea6 --- /dev/null +++ b/.changeset/selfish-numbers-cheat.md @@ -0,0 +1,5 @@ +--- +"@livekit/rtc-node": patch +--- + +Add method to query rtcStats on room via `await room.getRtcStats()` From 01dcc7ff85978470e4dff73a32f84376b1dc8680 Mon Sep 17 00:00:00 2001 From: lukasIO Date: Wed, 20 May 2026 14:33:04 +0200 Subject: [PATCH 4/4] fix imports --- packages/livekit-rtc/src/room.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/livekit-rtc/src/room.ts b/packages/livekit-rtc/src/room.ts index d5b2bb45..3958e72c 100644 --- a/packages/livekit-rtc/src/room.ts +++ b/packages/livekit-rtc/src/room.ts @@ -2,11 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 import { Mutex } from '@livekit/mutex'; -import { - EncryptionState, - type EncryptionType, - GetSessionStatsRequest, -} from '@livekit/rtc-ffi-bindings'; +import { EncryptionState, type EncryptionType } from '@livekit/rtc-ffi-bindings'; import type { FfiEvent, GetSessionStatsCallback, @@ -39,7 +35,6 @@ import { } from '@livekit/rtc-ffi-bindings'; import { TrackKind } from '@livekit/rtc-ffi-bindings'; import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter'; -import { randomInt } from 'crypto'; import EventEmitter from 'events'; import { ByteStreamReader, TextStreamReader } from './data_streams/stream_reader.js'; import type {