Merge branch 'voip-team/multi-SFU' of github.com:element-hq/element-call into voip-team/multi-SFU

This commit is contained in:
Robin
2025-08-28 13:48:53 +02:00
2 changed files with 37 additions and 34 deletions

View File

@@ -56,7 +56,7 @@ async function makeFocusInternal(
// Prioritize the .well-known/matrix/client, if available, over the configured SFU // Prioritize the .well-known/matrix/client, if available, over the configured SFU
const domain = rtcSession.room.client.getDomain(); const domain = rtcSession.room.client.getDomain();
if (localStorage.getItem("timo-focus-url")) { if (localStorage.getItem("timo-focus-url")) {
const timoFocusUrl = JSON.parse(localStorage.getItem("timo-focus-url")!); const timoFocusUrl = localStorage.getItem("timo-focus-url")!;
const focusFromUrl: LivekitFocus = { const focusFromUrl: LivekitFocus = {
type: "livekit", type: "livekit",
livekit_service_url: timoFocusUrl, livekit_service_url: timoFocusUrl,

View File

@@ -436,8 +436,8 @@ function getRoomMemberFromRtcMember(
return { id, member }; return { id, member };
} }
// TODO-MULTI-SFU Add all device syncing logic from useLivekit
class Connection { class Connection {
// TODO-MULTI-SFU Add all device syncing logic from useLivekit
private readonly sfuConfig = getSFUConfigWithOpenID( private readonly sfuConfig = getSFUConfigWithOpenID(
this.client, this.client,
this.serviceUrl, this.serviceUrl,
@@ -448,11 +448,6 @@ class Connection {
this.stopped = false; this.stopped = false;
const { url, jwt } = await this.sfuConfig; const { url, jwt } = await this.sfuConfig;
if (!this.stopped) await this.livekitRoom.connect(url, jwt); if (!this.stopped) await this.livekitRoom.connect(url, jwt);
// TODO-MULTI-SFU in this livekit room we really do not want to publish any tracks.
// this is only for testing purposes
const tracks = await this.livekitRoom.localParticipant.createTracks({
audio: { deviceId: "default" },
});
await this.livekitRoom.localParticipant.publishTrack(tracks[0]); await this.livekitRoom.localParticipant.publishTrack(tracks[0]);
} }
@@ -460,9 +455,10 @@ class Connection {
this.stopped = false; this.stopped = false;
const { url, jwt } = await this.sfuConfig; const { url, jwt } = await this.sfuConfig;
if (!this.stopped) await this.livekitRoom.connect(url, jwt); if (!this.stopped) await this.livekitRoom.connect(url, jwt);
if (!this.stopped) { if (!this.stopped) {
const tracks = await this.livekitRoom.localParticipant.createTracks({ const tracks = await this.livekitRoom.localParticipant.createTracks({
audio: { deviceId: "default" }, audio: true,
video: true, video: true,
}); });
for (const track of tracks) { for (const track of tracks) {
@@ -498,13 +494,15 @@ class Connection {
) )
.filter((f) => f.livekit_service_url === this.serviceUrl) .filter((f) => f.livekit_service_url === this.serviceUrl)
.map((f) => f.membership); .map((f) => f.membership);
return publishingMembers
.map((m) => const publishingP = publishingMembers
participants.find( .map((m) => {
(p) => p.identity === `${m.sender}:${m.deviceId}`, return participants.find((p) => {
), return p.identity === `${m.sender}:${m.deviceId}`;
) });
})
.filter((p): p is RemoteParticipant => !!p); .filter((p): p is RemoteParticipant => !!p);
return publishingP;
}), }),
), ),
[], [],
@@ -568,21 +566,22 @@ export class CallViewModel extends ViewModel {
), ),
); );
private readonly remoteConnections$ = combineLatest([ private readonly remoteConnections$ = this.scope.behavior(
this.localFocus, combineLatest([this.localFocus, this.foci$]).pipe(
this.foci$, accumulate(new Map<string, Connection>(), (prev, [localFocus, foci]) => {
]).pipe( const stopped = new Map(prev);
accumulate(new Map<string, Connection>(), (prev, [localFocus, foci]) => { const next = new Map<string, Connection>();
const stopped = new Map(prev); for (const focus of foci) {
const next = new Map<string, Connection>(); if (focus !== localFocus.livekit_service_url) {
stopped.delete(focus);
for (const focus of foci) { let nextConnection = prev.get(focus);
if (focus !== localFocus.livekit_service_url) { if (!nextConnection) {
stopped.delete(focus); logger.log(
next.set( "SFU remoteConnections$ construct new connection: ",
focus, focus,
prev.get(focus) ?? );
new Connection( nextConnection = new Connection(
new LivekitRoom({ new LivekitRoom({
...defaultLiveKitOptions, ...defaultLiveKitOptions,
e2ee: this.e2eeOptions, e2ee: this.e2eeOptions,
@@ -592,14 +591,18 @@ export class CallViewModel extends ViewModel {
this.matrixRTCSession.room.client, this.matrixRTCSession.room.client,
this.scope, this.scope,
this.matrixRTCSession, this.matrixRTCSession,
), );
); } else {
logger.log("SFU remoteConnections$ use prev connection: ", focus);
}
next.set(focus, nextConnection);
}
} }
}
for (const connection of stopped.values()) connection.stop(); for (const connection of stopped.values()) connection.stop();
return next; return next;
}), }),
),
); );
private readonly joined$ = new Subject<void>(); private readonly joined$ = new Subject<void>();