diff --git a/sdk/main.ts b/sdk/main.ts index 6cd37032d..3932d1190 100644 --- a/sdk/main.ts +++ b/sdk/main.ts @@ -231,7 +231,7 @@ export async function createMatrixRTCSdk( complete: () => { logger.info("Livekit room items subscription completed"); for (const item of callViewModel.livekitRoomItems$.value) { - logger.info("unregistering room item from room", item.url); + logger.info("unregistering room item from room", item.transport); item.livekitRoom.unregisterTextStreamHandler(TEXT_LK_TOPIC); } }, diff --git a/src/livekit/MatrixAudioRenderer.test.tsx b/src/livekit/MatrixAudioRenderer.test.tsx index e4573cda9..21183f866 100644 --- a/src/livekit/MatrixAudioRenderer.test.tsx +++ b/src/livekit/MatrixAudioRenderer.test.tsx @@ -24,6 +24,7 @@ import { testAudioContext } from "../useAudioContext.test"; import * as MediaDevicesContext from "../MediaDevicesContext"; import { LivekitRoomAudioRenderer } from "./MatrixAudioRenderer"; import { + exampleTransport, mockLocalParticipant, mockMediaDevices, mockRemoteParticipant, @@ -118,7 +119,7 @@ function renderTestComponent( p.identity)} livekitRoom={livekitRoom} - url={""} + transport={exampleTransport} /> , ); @@ -150,7 +151,7 @@ it("should not render or warn for the local participant", () => { , ); @@ -177,7 +178,7 @@ it("should warn only once per unexpected participant", () => { , ); diff --git a/src/livekit/MatrixAudioRenderer.tsx b/src/livekit/MatrixAudioRenderer.tsx index 95d68c056..c9c4bbb36 100644 --- a/src/livekit/MatrixAudioRenderer.tsx +++ b/src/livekit/MatrixAudioRenderer.tsx @@ -19,12 +19,10 @@ import { logger as rootLogger } from "matrix-js-sdk/lib/logger"; import { useEarpieceAudioConfig } from "../MediaDevicesContext"; import { useReactiveState } from "../useReactiveState"; import * as controls from "../controls"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; export interface MatrixAudioRendererProps { - /** - * The service URL of the LiveKit room. - */ - url: string; + transport: LivekitTransport; livekitRoom: LivekitRoom; /** * The list of participant identities to render audio for. @@ -54,7 +52,7 @@ export interface MatrixAudioRendererProps { * @public */ export function LivekitRoomAudioRenderer({ - url, + transport, livekitRoom, validIdentities, muted, @@ -88,7 +86,7 @@ export function LivekitRoomAudioRenderer({ warnedIdentities.current.add(identity); // Log that there is an invalid identity, that means that someone is publishing audio that is not expected to be in the call. logger.warn( - `Audio track ${identity} from ${url} has no matching matrix call member`, + `Audio track ${identity} from ${JSON.stringify(transport)} has no matching matrix call member`, `current members: ${validIdentities.join()}`, `track will not get rendered`, ); diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index b8d7ab89d..51c916f92 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -665,10 +665,10 @@ export const InCallView: FC = ({ onPointerOut={onPointerOut} > {header} - {audioParticipants.map(({ livekitRoom, url, participants }) => ( + {audioParticipants.map(({ livekitRoom, transport, participants }) => ( d.value)), ); - const livekitRoomItems$ = scope.behavior( - remoteMatrixLivekitMembers$.pipe( - switchMap((members) => { - const a$ = combineLatest( - members.value.map((member) => - combineLatest([member.connection$, member.participant.value$]).pipe( - map(([connection, participant]) => { - // do not render audio for local participant - if (!connection || !participant || participant.isLocal) - return null; - const livekitRoom = connection.livekitRoom; - const url = connection.transport.livekit_service_url; - return { - url, - livekitRoom, - participant: participant.identity, - }; - }), + const livekitRoomItems$ = scope.behavior( + remoteMatrixLivekitMembers$.pipe( + // Flatten the members to a snapshot of their current connections and + // participant objects + switchMap(({ value: members }) => { + if (members.length === 0) return of([]); + return combineLatest( + members.map((m) => + combineLatest( + [m.connection$, m.participant.value$], + (connection, participant) => ({ connection, participant }), ), ), ); - return a$; }), + // Keep only those with a defined connection and participant map((members) => - members.reduce((acc, curr) => { - if (!curr) return acc; - - const existing = acc.find((item) => item.url === curr.url); - if (existing) { - existing.participants.push(curr.participant); - } else { - acc.push({ - livekitRoom: curr.livekitRoom, - participants: [curr.participant], - url: curr.url, - }); - } - return acc; - }, []), + members.filter( + ( + m, + ): m is { connection: Connection; participant: RemoteParticipant } => + m.connection !== null && m.participant !== null, + ), + ), + // Group members by connection + map((members) => + [...Map.groupBy(members, (m) => m.connection)].map( + ([connection, members]) => ({ + transport: connection.transport, + livekitRoom: connection.livekitRoom, + participants: members.map((m) => m.participant.identity), + }), + ), ), ), - [], ); const handsRaised$ = scope.behavior(