make audio work

Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Timo K
2025-09-16 11:31:47 +02:00
parent cc870c3cc2
commit 38d78ddce4
5 changed files with 48 additions and 13 deletions

View File

@@ -18,7 +18,7 @@ import { useTracks } from "@livekit/components-react";
import { testAudioContext } from "../useAudioContext.test"; import { testAudioContext } from "../useAudioContext.test";
import * as MediaDevicesContext from "../MediaDevicesContext"; import * as MediaDevicesContext from "../MediaDevicesContext";
import { MatrixAudioRenderer } from "./MatrixAudioRenderer"; import { LivekitRoomAudioRenderer } from "./MatrixAudioRenderer";
import { mockMediaDevices, mockTrack } from "../utils/test"; import { mockMediaDevices, mockTrack } from "../utils/test";
export const TestAudioContextConstructor = vi.fn(() => testAudioContext); export const TestAudioContextConstructor = vi.fn(() => testAudioContext);
@@ -54,7 +54,7 @@ vi.mocked(useTracks).mockReturnValue(tracks);
it("should render for member", () => { it("should render for member", () => {
const { container, queryAllByTestId } = render( const { container, queryAllByTestId } = render(
<MediaDevicesProvider value={mockMediaDevices({})}> <MediaDevicesProvider value={mockMediaDevices({})}>
<MatrixAudioRenderer <LivekitRoomAudioRenderer
members={[{ sender: "test", deviceId: "123" }] as CallMembership[]} members={[{ sender: "test", deviceId: "123" }] as CallMembership[]}
/> />
</MediaDevicesProvider>, </MediaDevicesProvider>,
@@ -69,7 +69,7 @@ it("should not render without member", () => {
] as CallMembership[]; ] as CallMembership[];
const { container, queryAllByTestId } = render( const { container, queryAllByTestId } = render(
<MediaDevicesProvider value={mockMediaDevices({})}> <MediaDevicesProvider value={mockMediaDevices({})}>
<MatrixAudioRenderer members={memberships} /> <LivekitRoomAudioRenderer members={memberships} />
</MediaDevicesProvider>, </MediaDevicesProvider>,
); );
expect(container).toBeTruthy(); expect(container).toBeTruthy();
@@ -79,7 +79,7 @@ it("should not render without member", () => {
it("should not setup audioContext gain and pan if there is no need to.", () => { it("should not setup audioContext gain and pan if there is no need to.", () => {
render( render(
<MediaDevicesProvider value={mockMediaDevices({})}> <MediaDevicesProvider value={mockMediaDevices({})}>
<MatrixAudioRenderer <LivekitRoomAudioRenderer
members={[{ sender: "test", deviceId: "123" }] as CallMembership[]} members={[{ sender: "test", deviceId: "123" }] as CallMembership[]}
/> />
</MediaDevicesProvider>, </MediaDevicesProvider>,
@@ -102,7 +102,7 @@ it("should setup audioContext gain and pan", () => {
}); });
render( render(
<MediaDevicesProvider value={mockMediaDevices({})}> <MediaDevicesProvider value={mockMediaDevices({})}>
<MatrixAudioRenderer <LivekitRoomAudioRenderer
members={[{ sender: "test", deviceId: "123" }] as CallMembership[]} members={[{ sender: "test", deviceId: "123" }] as CallMembership[]}
/> />
</MediaDevicesProvider>, </MediaDevicesProvider>,

View File

@@ -6,6 +6,7 @@ Please see LICENSE in the repository root for full details.
*/ */
import { getTrackReferenceId } from "@livekit/components-core"; import { getTrackReferenceId } from "@livekit/components-core";
import { type Room as LivekitRoom } from "livekit-client";
import { type RemoteAudioTrack, Track } from "livekit-client"; import { type RemoteAudioTrack, Track } from "livekit-client";
import { useEffect, useMemo, useRef, useState, type ReactNode } from "react"; import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
import { import {
@@ -19,7 +20,7 @@ import { logger } from "matrix-js-sdk/lib/logger";
import { useEarpieceAudioConfig } from "../MediaDevicesContext"; import { useEarpieceAudioConfig } from "../MediaDevicesContext";
import { useReactiveState } from "../useReactiveState"; import { useReactiveState } from "../useReactiveState";
import * as controls from "../controls"; import * as controls from "../controls";
import {} from "@livekit/components-core";
export interface MatrixAudioRendererProps { export interface MatrixAudioRendererProps {
/** /**
* The list of participants to render audio for. * The list of participants to render audio for.
@@ -27,6 +28,7 @@ export interface MatrixAudioRendererProps {
* that are not expected to be in the rtc session. * that are not expected to be in the rtc session.
*/ */
members: CallMembership[]; members: CallMembership[];
livekitRoom: LivekitRoom;
/** /**
* If set to `true`, mutes all audio tracks rendered by the component. * If set to `true`, mutes all audio tracks rendered by the component.
* @remarks * @remarks
@@ -49,9 +51,10 @@ export interface MatrixAudioRendererProps {
* ``` * ```
* @public * @public
*/ */
export function MatrixAudioRenderer({ export function LivekitRoomAudioRenderer({
members, members,
muted, muted,
livekitRoom,
}: MatrixAudioRendererProps): ReactNode { }: MatrixAudioRendererProps): ReactNode {
const validIdentities = useMemo( const validIdentities = useMemo(
() => () =>
@@ -89,6 +92,7 @@ export function MatrixAudioRenderer({
{ {
updateOnlyOn: [], updateOnlyOn: [],
onlySubscribed: true, onlySubscribed: true,
room: livekitRoom,
}, },
).filter((ref) => { ).filter((ref) => {
const isValid = validIdentities?.has(ref.participant.identity); const isValid = validIdentities?.has(ref.participant.identity);

View File

@@ -45,7 +45,7 @@ import {
} from "../settings/settings"; } from "../settings/settings";
import { ReactionsSenderProvider } from "../reactions/useReactionsSender"; import { ReactionsSenderProvider } from "../reactions/useReactionsSender";
import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement"; import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement";
import { MatrixAudioRenderer } from "../livekit/MatrixAudioRenderer"; import { LivekitRoomAudioRenderer } from "../livekit/MatrixAudioRenderer";
import { MediaDevicesContext } from "../MediaDevicesContext"; import { MediaDevicesContext } from "../MediaDevicesContext";
import { HeaderStyle } from "../UrlParams"; import { HeaderStyle } from "../UrlParams";
@@ -88,7 +88,7 @@ beforeEach(() => {
// MatrixAudioRenderer is tested separately. // MatrixAudioRenderer is tested separately.
( (
MatrixAudioRenderer as MockedFunction<typeof MatrixAudioRenderer> LivekitRoomAudioRenderer as MockedFunction<typeof LivekitRoomAudioRenderer>
).mockImplementation((_props) => { ).mockImplementation((_props) => {
return <div>mocked: MatrixAudioRenderer</div>; return <div>mocked: MatrixAudioRenderer</div>;
}); });

View File

@@ -106,6 +106,7 @@ import {
} from "../settings/settings"; } from "../settings/settings";
import { ReactionsReader } from "../reactions/ReactionsReader"; import { ReactionsReader } from "../reactions/ReactionsReader";
import { useTypedEventEmitter } from "../useEvents.ts"; import { useTypedEventEmitter } from "../useEvents.ts";
import { LivekitRoomAudioRenderer } from "../livekit/MatrixAudioRenderer.tsx";
import { muteAllAudio$ } from "../state/MuteAllAudioModel.ts"; import { muteAllAudio$ } from "../state/MuteAllAudioModel.ts";
import { useMediaDevices } from "../MediaDevicesContext.ts"; import { useMediaDevices } from "../MediaDevicesContext.ts";
import { EarpieceOverlay } from "./EarpieceOverlay.tsx"; import { EarpieceOverlay } from "./EarpieceOverlay.tsx";
@@ -151,7 +152,6 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
}, },
reactionsReader.raisedHands$, reactionsReader.raisedHands$,
reactionsReader.reactions$, reactionsReader.reactions$,
props.e2eeSystem,
); );
setVm(vm); setVm(vm);
return (): void => { return (): void => {
@@ -746,6 +746,8 @@ export const InCallView: FC<InCallViewProps> = ({
matrixRoom.roomId, matrixRoom.roomId,
); );
const allLivekitRooms = useBehavior(vm.allLivekitRooms$);
const memberships = useBehavior(vm.memberships$);
const toggleScreensharing = useCallback(() => { const toggleScreensharing = useCallback(() => {
throw new Error("TODO-MULTI-SFU"); throw new Error("TODO-MULTI-SFU");
// localParticipant // localParticipant
@@ -878,7 +880,14 @@ export const InCallView: FC<InCallViewProps> = ({
</Text> </Text>
) )
} }
{/* TODO-MULTI-SFU: <MatrixAudioRenderer members={memberships} muted={muteAllAudio} /> */} {allLivekitRooms.map((roomItem) => (
<LivekitRoomAudioRenderer
key={roomItem.url}
livekitRoom={roomItem.room}
members={memberships}
muted={muteAllAudio}
/>
))}
{renderContent()} {renderContent()}
<CallEventAudioRenderer vm={vm} muted={muteAllAudio} /> <CallEventAudioRenderer vm={vm} muted={muteAllAudio} />
<ReactionsAudioRenderer vm={vm} muted={muteAllAudio} /> <ReactionsAudioRenderer vm={vm} muted={muteAllAudio} />

View File

@@ -116,7 +116,6 @@ import { type Behavior } from "./Behavior";
import { import {
enterRTCSession, enterRTCSession,
getLivekitAlias, getLivekitAlias,
leaveRTCSession,
makeFocus, makeFocus,
} from "../rtcSessionHelpers"; } from "../rtcSessionHelpers";
import { E2eeType } from "../e2ee/e2eeType"; import { E2eeType } from "../e2ee/e2eeType";
@@ -462,7 +461,10 @@ export class CallViewModel extends ViewModel {
), ),
); );
private readonly memberships$ = this.scope.behavior( // TODO-MULTI-SFU make sure that we consider the room memberships here as well (so that here we only have valid memberships)
// this also makes it possible to use this memberships$ list in all observables based on it.
// there should be no other call to: this.matrixRTCSession.memberships!
public readonly memberships$ = this.scope.behavior(
fromEvent( fromEvent(
this.matrixRTCSession, this.matrixRTCSession,
MatrixRTCSessionEvent.MembershipsChanged, MatrixRTCSessionEvent.MembershipsChanged,
@@ -567,6 +569,26 @@ export class CallViewModel extends ViewModel {
concatMap(({ stop }) => stop), concatMap(({ stop }) => stop),
); );
public readonly allLivekitRooms$ = this.scope.behavior(
combineLatest([
this.remoteConnections$,
this.localConnection,
this.localFocus,
]).pipe(
map(([remoteConnections, localConnection, localFocus]) =>
Array.from(remoteConnections.entries())
.map(([index, c]) => ({ room: c.livekitRoom, url: index }))
.concat([
{
room: localConnection.livekitRoom,
url: localFocus.livekit_service_url,
},
]),
),
startWith([]),
),
);
private readonly userId = this.matrixRoom.client.getUserId(); private readonly userId = this.matrixRoom.client.getUserId();
private readonly matrixConnected$ = this.scope.behavior( private readonly matrixConnected$ = this.scope.behavior(