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()` 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 b0a89dd2..3958e72c 100644 --- a/packages/livekit-rtc/src/room.ts +++ b/packages/livekit-rtc/src/room.ts @@ -3,7 +3,11 @@ // 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 type { + FfiEvent, + GetSessionStatsCallback, + GetSessionStatsResponse, +} from '@livekit/rtc-ffi-bindings'; import { DisconnectReason, type OwnedParticipant } from '@livekit/rtc-ffi-bindings'; import type { DataStream_Trailer, @@ -191,6 +195,36 @@ 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'); + } + // 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 cbPromise; + 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; }