mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-16 23:45:50 +00:00
[SDK] Match LiveKit participants against all backend identities
A member authenticated through the legacy JWT endpoint publishes under a `user:device` identity, while one authenticated through the default endpoint publishes under a hashed identity. Both are listed in the membership's backend identities, so match against the whole list rather than only the primary one. On the SDK this is RTCMember.backendIdentities, which the LiveKit backend matches media and data packets against; the harness keys its tiles by the membership id, which the backend identity no longer tells apart. (cherry picked from commit c9aad2a2b3279782d101c9bc6c80af0417ccbb92)
This commit is contained in:
+3
-3
@@ -185,14 +185,14 @@ function showMembers(
|
|||||||
.subscribe(([local, remote]) => {
|
.subscribe(([local, remote]) => {
|
||||||
const current = local === null ? remote : [local, ...remote];
|
const current = local === null ? remote : [local, ...remote];
|
||||||
for (const [id, tile] of tiles)
|
for (const [id, tile] of tiles)
|
||||||
if (!current.some((m) => m.rtcBackendIdentity === id)) {
|
if (!current.some((m) => m.memberId === id)) {
|
||||||
tile.remove();
|
tile.remove();
|
||||||
tiles.delete(id);
|
tiles.delete(id);
|
||||||
}
|
}
|
||||||
for (const member of current)
|
for (const member of current)
|
||||||
if (!tiles.has(member.rtcBackendIdentity)) {
|
if (!tiles.has(member.memberId)) {
|
||||||
const tile = memberTile(scope, member);
|
const tile = memberTile(scope, member);
|
||||||
tiles.set(member.rtcBackendIdentity, tile);
|
tiles.set(member.memberId, tile);
|
||||||
members.append(tile);
|
members.append(tile);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -218,8 +218,8 @@ export function createRTCParticipation(
|
|||||||
// ever hears from attested members
|
// ever hears from attested members
|
||||||
const data$ = backend.data$.pipe(
|
const data$ = backend.data$.pipe(
|
||||||
map(({ senderId, topic, text }): DataMessage | null => {
|
map(({ senderId, topic, text }): DataMessage | null => {
|
||||||
const member = remoteMembers$.value.find(
|
const member = remoteMembers$.value.find((m) =>
|
||||||
(m) => m.rtcBackendIdentity === senderId,
|
m.backendIdentities.includes(senderId),
|
||||||
);
|
);
|
||||||
if (member === undefined) {
|
if (member === undefined) {
|
||||||
logger.warn(`Dropping data from ${senderId}: not a member`);
|
logger.warn(`Dropping data from ${senderId}: not a member`);
|
||||||
|
|||||||
+13
-9
@@ -259,20 +259,24 @@ export type ResolvedTransport =
|
|||||||
export interface RTCMember {
|
export interface RTCMember {
|
||||||
local: boolean;
|
local: boolean;
|
||||||
/**
|
/**
|
||||||
* The identity this member has on the media backend's transport: the
|
* The identity this member is known by on its transport for matching
|
||||||
* participant identity the SFU sees, and the sender of its data packets. The
|
* purposes, read off the membership: `${userId}:${deviceId}`. A stable key
|
||||||
* SDK uses it to match what arrives on the transport back to the member; a
|
* per membership and a debug label for a host; the SDK matches media
|
||||||
* host needs it only as a stable key per membership or as a debug label.
|
* against `backendIdentities`, not this.
|
||||||
*
|
|
||||||
* Today it is read off the membership, where the js-sdk computes it:
|
|
||||||
* `${userId}:${deviceId}` before sticky events, a hash of `memberId` in
|
|
||||||
* Matrix 2.0 mode. Eventually the backend will derive it itself, so treat it
|
|
||||||
* as opaque.
|
|
||||||
*/
|
*/
|
||||||
rtcBackendIdentity: string;
|
rtcBackendIdentity: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
deviceId: string;
|
deviceId: string;
|
||||||
/** The membership's own id; what the Matrix 2.0 identity is derived from. */
|
/** The membership's own id; what the Matrix 2.0 identity is derived from. */
|
||||||
|
/**
|
||||||
|
* Every identity this member may publish under on its transport: the
|
||||||
|
* participant identity the SFU sees, and the sender of its data packets.
|
||||||
|
* One per authentication flow the member's transport allows, so a
|
||||||
|
* transport with both a `url` and a `livekit_service_url` lists the hashed
|
||||||
|
* and the legacy identity, and the backend matches whichever shows up.
|
||||||
|
* Opaque to a host.
|
||||||
|
*/
|
||||||
|
backendIdentities: readonly string[];
|
||||||
memberId: string;
|
memberId: string;
|
||||||
/** Which transport this member is on; undefined when the membership has none. */
|
/** Which transport this member is on; undefined when the membership has none. */
|
||||||
transport$: Behavior<TransportMetadata | undefined>;
|
transport$: Behavior<TransportMetadata | undefined>;
|
||||||
|
|||||||
@@ -46,12 +46,14 @@ export function createRTCMember(
|
|||||||
membership$: Behavior<CallMembership>,
|
membership$: Behavior<CallMembership>,
|
||||||
{ metadata, transports, own }: MemberContext,
|
{ metadata, transports, own }: MemberContext,
|
||||||
): RTCMember {
|
): RTCMember {
|
||||||
const { userId, deviceId, memberId, rtcBackendIdentity } = membership$.value;
|
const { userId, deviceId, memberId, rtcBackendIdentity, backendIdentities } =
|
||||||
|
membership$.value;
|
||||||
return {
|
return {
|
||||||
local: userId === own.userId && deviceId === own.deviceId,
|
local: userId === own.userId && deviceId === own.deviceId,
|
||||||
// Read off the membership for now; the backend will derive it once the
|
// Read off the membership for now; the backend will derive them once the
|
||||||
// identity scheme is its own and not the js-sdk's
|
// identity scheme is its own and not the js-sdk's
|
||||||
rtcBackendIdentity,
|
rtcBackendIdentity,
|
||||||
|
backendIdentities,
|
||||||
userId,
|
userId,
|
||||||
deviceId,
|
deviceId,
|
||||||
memberId,
|
memberId,
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ export function mockRtcMembership(
|
|||||||
deviceId: string,
|
deviceId: string,
|
||||||
customOverwrites?: {
|
customOverwrites?: {
|
||||||
rtcBackendIdentity?: string;
|
rtcBackendIdentity?: string;
|
||||||
|
/** Every identity the member may have on its transport, where more than one. */
|
||||||
|
backendIdentities?: string[];
|
||||||
callId?: string;
|
callId?: string;
|
||||||
fociPreferred?: Transport[];
|
fociPreferred?: Transport[];
|
||||||
focusActive?: LivekitFocusSelection;
|
focusActive?: LivekitFocusSelection;
|
||||||
@@ -199,11 +201,12 @@ export function mockRtcMembership(
|
|||||||
content: data,
|
content: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
const membershipData = CallMembership.membershipDataFromMatrixEvent(event);
|
const backendIdentity = rtcBackendIdentity ?? `${userId}:${deviceId}`;
|
||||||
const cms = new CallMembership(
|
const cms = new CallMembership(
|
||||||
event,
|
event,
|
||||||
membershipData,
|
CallMembership.membershipDataFromMatrixEvent(event),
|
||||||
rtcBackendIdentity ?? `${userId}:${deviceId}`,
|
customOverwrites?.backendIdentities ?? [backendIdentity],
|
||||||
|
backendIdentity,
|
||||||
);
|
);
|
||||||
vi.mocked(cms).getTransport = vi.fn().mockReturnValue(fociPreferred[0]);
|
vi.mocked(cms).getTransport = vi.fn().mockReturnValue(fociPreferred[0]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user