changes summary valere timo

This commit is contained in:
Timo K
2025-10-30 22:15:35 +01:00
parent 4f892e358a
commit a44171da1c
3 changed files with 86 additions and 42 deletions

View File

@@ -19,10 +19,21 @@ import {
SyncState, SyncState,
type Room as MatrixRoom, type Room as MatrixRoom,
} from "matrix-js-sdk"; } from "matrix-js-sdk";
import { fromEvent, map, type Observable, scan, startWith } from "rxjs"; import {
BehaviorSubject,
combineLatest,
from,
fromEvent,
map,
type Observable,
of,
scan,
startWith,
switchMap,
} from "rxjs";
import { multiSfu } from "../../settings/settings"; import { multiSfu } from "../../settings/settings";
import { type Behavior } from "../Behavior"; import { type Behavior } from "../Behavior";
import { type ConnectionManager } from "../remoteMembers/ConnectionManager"; import { ConnectionManager } from "../remoteMembers/ConnectionManager";
import { makeTransport } from "../../rtcSessionHelpers"; import { makeTransport } from "../../rtcSessionHelpers";
import { type ObservableScope } from "../ObservableScope"; import { type ObservableScope } from "../ObservableScope";
import { async$, unwrapAsync } from "../Async"; import { async$, unwrapAsync } from "../Async";
@@ -31,7 +42,19 @@ import { type MuteStates } from "../MuteStates";
import { type ProcessorState } from "../../livekit/TrackProcessorContext"; import { type ProcessorState } from "../../livekit/TrackProcessorContext";
import { type MediaDevices } from "../../state/MediaDevices"; import { type MediaDevices } from "../../state/MediaDevices";
import { and$ } from "../../utils/observable"; import { and$ } from "../../utils/observable";
import { areLivekitTransportsEqual } from "../remoteMembers/matrixLivekitMerger";
/*
* - get well known
* - get oldest membership
* - get transport to use
* - get openId + jwt token
* - wait for createTrack() call
* - create tracks
* - wait for join() call
* - Publisher.publishTracks()
* - send join state/sticky event
*/
interface Props { interface Props {
scope: ObservableScope; scope: ObservableScope;
mediaDevices: MediaDevices; mediaDevices: MediaDevices;
@@ -71,10 +94,18 @@ export const ownMembership$ = ({
roomId, roomId,
trackerProcessorState$, trackerProcessorState$,
}: Props): { }: Props): {
connected$: Behavior<boolean>; // publisher: Publisher
transport$: Behavior<LivekitTransport | null>; requestJoin(): Observable<JoinedStateWithErrors>;
publisher: Publisher; startTracks(): Track[];
} => { } => {
// This should be used in a combineLatest with publisher$ to connect.
const shouldStartTracks$ = BehaviorSubject(false);
// to make it possible to call startTracks before the preferredTransport$ has resolved.
const startTracks = () => {
shouldStartTracks$.next(true);
};
const userId = client.getUserId()!; const userId = client.getUserId()!;
const deviceId = client.getDeviceId()!; const deviceId = client.getDeviceId()!;
const multiSfu$ = multiSfu.value$; const multiSfu$ = multiSfu.value$;
@@ -82,22 +113,23 @@ export const ownMembership$ = ({
* The transport that we would personally prefer to publish on (if not for the * The transport that we would personally prefer to publish on (if not for the
* transport preferences of others, perhaps). * transport preferences of others, perhaps).
*/ */
const preferredTransport$ = scope.behavior( const preferredTransport$: Behavior<LivekitTransport> = scope.behavior(
async$(makeTransport(client, roomId)).pipe( from(makeTransport(client, roomId)),
map(unwrapAsync<LivekitTransport | null>(null)),
),
); );
const connection = connectionManager.registerTransports( connectionManager.registerTransports(
scope.behavior(preferredTransport$.pipe(map((t) => (t ? [t] : [])))), scope.behavior(preferredTransport$.pipe(map((t) => (t ? [t] : [])))),
)[0]; );
if (!connection) {
logger.warn(
"No connection found when passing transport to connectionManager. transport:",
preferredTransport$.value,
);
}
const connection$ = scope.behavior(
combineLatest([connectionManager.connections$, preferredTransport$]).pipe(
map(([connections, transport]) =>
connections.find((connection) =>
areLivekitTransportsEqual(connection.transport, transport),
),
),
),
);
/** /**
* Whether we are connected to the MatrixRTC session. * Whether we are connected to the MatrixRTC session.
*/ */
@@ -129,6 +161,7 @@ export const ownMembership$ = ({
), ),
), ),
); );
/** /**
* Whether we are "fully" connected to the call. Accounts for both the * Whether we are "fully" connected to the call. Accounts for both the
* connection to the MatrixRTC session and the LiveKit publish connection. * connection to the MatrixRTC session and the LiveKit publish connection.
@@ -136,19 +169,31 @@ export const ownMembership$ = ({
const connected$ = scope.behavior( const connected$ = scope.behavior(
and$( and$(
matrixConnected$, matrixConnected$,
connection.state$.pipe( connection$.pipe(
map((state) => state.state === "ConnectedToLkRoom"), switchMap((c) =>
c
? c.state$.pipe(map((state) => state.state === "ConnectedToLkRoom"))
: of(false),
),
), ),
), ),
); );
const publisher = new Publisher( const publisher = scope.behavior(
scope, connection$.pipe(
connection, map((c) =>
mediaDevices, c
muteStates, ? new Publisher(
e2eeLivekitOptions, scope,
trackerProcessorState$, c,
mediaDevices,
muteStates,
e2eeLivekitOptions,
trackerProcessorState$,
)
: null,
),
),
); );
// HOW IT WAS PREVIEOUSLY CREATED // HOW IT WAS PREVIEOUSLY CREATED
@@ -171,11 +216,11 @@ export const ownMembership$ = ({
* null when not joined. * null when not joined.
*/ */
// DISCUSSION ownMembershipManager // DISCUSSION ownMembershipManager
const localTransport$: Behavior<Async<LivekitTransport> | null> = const localTransport$: Behavior<LivekitTransport | null> =
this.scope.behavior( this.scope.behavior(
this.transports$.pipe( this.transports$.pipe(
map((transports) => transports?.local ?? null), map((transports) => transports?.local ?? null),
distinctUntilChanged<Async<LivekitTransport> | null>(deepCompare), distinctUntilChanged<LivekitTransport | null>(deepCompare),
), ),
); );

View File

@@ -99,7 +99,7 @@ export class ConnectionManager {
/** /**
* Connections for each transport in use by one or more session members. * Connections for each transport in use by one or more session members.
*/ */
private readonly connections$ = this.scope.behavior( public readonly connections$ = this.scope.behavior(
generateKeyed$<LivekitTransport[], Connection, Connection[]>( generateKeyed$<LivekitTransport[], Connection, Connection[]>(
this.transports$, this.transports$,
(transports, createOrGet) => { (transports, createOrGet) => {
@@ -144,22 +144,20 @@ export class ConnectionManager {
* the same `transports$` behavior reference. * the same `transports$` behavior reference.
* @param transports$ The Behavior containing a list of transports to subscribe to. * @param transports$ The Behavior containing a list of transports to subscribe to.
*/ */
public registerTransports( public registerTransports(transports$: Behavior<LivekitTransport[]>): void {
transports$: Behavior<LivekitTransport[]>,
): Connection[] {
if (!this.transportsSubscriptions$.value.some((t$) => t$ === transports$)) { if (!this.transportsSubscriptions$.value.some((t$) => t$ === transports$)) {
this.transportsSubscriptions$.next( this.transportsSubscriptions$.next(
this.transportsSubscriptions$.value.concat(transports$), this.transportsSubscriptions$.value.concat(transports$),
); );
} }
// After updating the subscriptions our connection list is also updated. // // After updating the subscriptions our connection list is also updated.
return transports$.value // return transports$.value
.map((transport) => { // .map((transport) => {
const isConnectionForTransport = (connection: Connection): boolean => // const isConnectionForTransport = (connection: Connection): boolean =>
areLivekitTransportsEqual(connection.transport, transport); // areLivekitTransportsEqual(connection.transport, transport);
return this.connections$.value.find(isConnectionForTransport); // return this.connections$.value.find(isConnectionForTransport);
}) // })
.filter((c) => c !== undefined); // .filter((c) => c !== undefined);
} }
/** /**
@@ -218,7 +216,7 @@ export class ConnectionManager {
* Each participant that is found on all connections managed by the manager will be listed. * Each participant that is found on all connections managed by the manager will be listed.
* *
* They are stored an a map keyed by `participant.identity` * They are stored an a map keyed by `participant.identity`
* (which is equivalent to the `member.id` field in the `m.rtc.member` event) * TODO (which is equivalent to the `member.id` field in the `m.rtc.member` event) right now its userId:deviceId
*/ */
public allParticipantsByMemberId$ = this.scope.behavior( public allParticipantsByMemberId$ = this.scope.behavior(
this.allParticipantsWithConnection$.pipe( this.allParticipantsWithConnection$.pipe(

View File

@@ -109,7 +109,8 @@ export class MatrixLivekitMerger {
const items: MatrixLivekitItem[] = memberships.map( const items: MatrixLivekitItem[] = memberships.map(
({ membership, transport }) => { ({ membership, transport }) => {
const participantsWithConnection = participantsByMemberId.get( const participantsWithConnection = participantsByMemberId.get(
membership.membershipID, // membership.membershipID, Currently its hardcoded by the jwt service to
`${membership.userId}:${membership.deviceId}`,
); );
const participant = const participant =
transport && transport &&