From b0659279ee39c4a004fd7b0e135b4e8789706e2a Mon Sep 17 00:00:00 2001 From: "Timo K." Date: Thu, 8 Oct 2026 16:35:29 +0200 Subject: [PATCH] update to sdk api change: audioLevel stats$() --- src/state/media/MediaViewModel.test.ts | 11 ++--------- src/state/media/MemberMediaViewModel.ts | 5 ++++- src/state/media/UserMediaViewModel.ts | 5 ++++- src/utils/test.ts | 3 ++- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/state/media/MediaViewModel.test.ts b/src/state/media/MediaViewModel.test.ts index 2abe2e9f3..c53bb34a0 100644 --- a/src/state/media/MediaViewModel.test.ts +++ b/src/state/media/MediaViewModel.test.ts @@ -6,11 +6,7 @@ Please see LICENSE in the repository root for full details. */ import { expect, onTestFinished, test, vi } from "vitest"; -import { - type Behavior, - constant, - type MediaStreamStats, -} from "@element-hq/matrixrtc-sdk"; +import { constant } from "@element-hq/matrixrtc-sdk"; import { waitFor } from "@testing-library/dom"; import { BehaviorSubject } from "rxjs"; @@ -241,12 +237,9 @@ test("user media polls stream stats only while the setting is on", () => { onTestFinished(() => showConnectionStats.setValue(false)); withTestScheduler(({ cold, expectObservable, schedule }) => { const stats = { type: "inbound-rtp" } as RTCInboundRtpStreamStats; - // A behavior that only runs its source while subscribed, as the SDK's does const vm = mockRemoteMedia(rtcMembership, {}, [ mockAudioTrack({ - stats$: cold("-s", { - s: stats, - }) as unknown as Behavior, + stats$: () => cold("-s", { s: stats }), }), ]); schedule("-a-b", { diff --git a/src/state/media/MemberMediaViewModel.ts b/src/state/media/MemberMediaViewModel.ts index a75588f3a..51864878b 100644 --- a/src/state/media/MemberMediaViewModel.ts +++ b/src/state/media/MemberMediaViewModel.ts @@ -35,6 +35,9 @@ import { type UserMediaViewModel } from "./UserMediaViewModel"; import { type ScreenShareViewModel } from "./ScreenShareViewModel"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; +/** How often a view model reads a track's RTP statistics while it needs them. */ +export const statsIntervalMs = 1000; + // TODO: Encryption status is kinda broken and thus unused right now. Remove? export enum EncryptionStatus { Connecting, @@ -157,7 +160,7 @@ function receivingOkay$( ): Observable { let last: { framesDecoded?: number; framesReceived?: number } = {}; return track$.pipe( - switchMap((track) => track?.stats$ ?? of(undefined)), + switchMap((track) => track?.stats$(statsIntervalMs) ?? of(undefined)), map((stats): boolean | undefined => { if (stats === undefined || stats.type !== "inbound-rtp") return undefined; const { framesDecoded, framesReceived } = diff --git a/src/state/media/UserMediaViewModel.ts b/src/state/media/UserMediaViewModel.ts index 6be71593e..5b376b920 100644 --- a/src/state/media/UserMediaViewModel.ts +++ b/src/state/media/UserMediaViewModel.ts @@ -27,6 +27,7 @@ import { createMemberMedia, type MemberMediaInputs, type BaseMemberMediaViewModel, + statsIntervalMs, } from "./MemberMediaViewModel"; import { type RemoteUserMediaViewModel } from "./RemoteUserMediaViewModel"; import { showConnectionStats } from "../../settings/settings"; @@ -104,7 +105,9 @@ export function createBaseUserMedia( showConnectionStats.value$, ]).pipe( switchMap(([track, show]) => - track !== undefined && show ? track.stats$ : of(undefined), + track !== undefined && show + ? track.stats$(statsIntervalMs) + : of(undefined), ), ), ); diff --git a/src/utils/test.ts b/src/utils/test.ts index 205055b60..f59eecd7e 100644 --- a/src/utils/test.ts +++ b/src/utils/test.ts @@ -311,8 +311,9 @@ export function mockMediaTrack( id: "track", muted$: constant(false), encrypted: true, - stats$: constant(undefined), + stats$: () => constant(undefined), isActive$: constant(false), + audioLevel$: constant(0), attach: vi.fn(), detach: vi.fn(), setAudioContext: vi.fn(),