This commit is contained in:
Valere
2025-12-16 13:40:06 +01:00
parent 190cdfcb60
commit 80e760ca55
3 changed files with 32 additions and 43 deletions

View File

@@ -37,7 +37,7 @@ import {
import { type Logger } from "matrix-js-sdk/lib/logger";
import { deepCompare } from "matrix-js-sdk/lib/utils";
import { constant, type Behavior } from "../../Behavior.ts";
import { type Behavior } from "../../Behavior.ts";
import { type IConnectionManager } from "../remoteMembers/ConnectionManager.ts";
import { type ObservableScope } from "../../ObservableScope.ts";
import { type Publisher } from "./Publisher.ts";
@@ -68,6 +68,8 @@ export enum TransportState {
export enum PublishState {
WaitingForUser = "publish_waiting_for_user",
// XXX: This state is removed for now since we do not have full control over
// track publication anymore with the publisher abstraction, might come back in the future?
// /** Implies lk connection is connected */
// Starting = "publish_start_publishing",
/** Implies lk connection is connected */
@@ -79,6 +81,8 @@ export enum PublishState {
export enum TrackState {
/** The track is waiting for user input to create tracks (waiting to call `startTracks()`) */
WaitingForUser = "tracks_waiting_for_user",
// XXX: This state is removed for now since we do not have full control over
// track creation anymore with the publisher abstraction, might come back in the future?
// /** Implies lk connection is connected */
// Creating = "tracks_creating",
/** Implies lk connection is connected */
@@ -154,9 +158,10 @@ export const createLocalMembership$ = ({
matrixRTCSession,
}: Props): {
/**
* This starts audio and video tracks. They will be reused when calling `requestPublish`.
* This request to start audio and video tracks.
* Can be called early to pre-emptively get media permissions and start devices.
*/
startTracks: () => Behavior<void>;
startTracks: () => void;
/**
* This sets a inner state (shouldPublish) to true and instructs the js-sdk and livekit to keep the user
* connected to matrix and livekit.
@@ -265,19 +270,10 @@ export const createLocalMembership$ = ({
* The publisher is stored in here an abstracts creating and publishing tracks.
*/
const publisher$ = new BehaviorSubject<Publisher | null>(null);
/**
* Extract the tracks from the published. Also reacts to changing publishers.
*/
// const tracks$ = scope.behavior(
// publisher$.pipe(switchMap((p) => (p?.tracks$ ? p.tracks$ : constant([])))),
// );
// const publishing$ = scope.behavior(
// publisher$.pipe(switchMap((p) => p?.publishing$ ?? constant(false))),
// );
const startTracks = (): Behavior<void> => {
const startTracks = (): void => {
trackStartRequested.resolve();
return constant(undefined);
// This used to return the tracks, but now they are only accessible via the publisher.
};
const requestJoinAndPublish = (): void => {
@@ -348,14 +344,6 @@ export const createLocalMembership$ = ({
setPublishError(new UnknownCallError(error as Error));
}
}
// XXX Why is that?
// else {
// try {
// await publisher?.stopPublishing();
// } catch (error) {
// setLivekitError(new UnknownCallError(error as Error));
// }
// }
},
);
@@ -401,16 +389,10 @@ export const createLocalMembership$ = ({
([
localConnectionState,
localTransport,
// tracks,
// publishing,
shouldPublish,
shouldStartTracks,
]) => {
if (!localTransport) return null;
// const hasTracks = tracks.length > 0;
// let trackState: TrackState = TrackState.WaitingForUser;
// if (hasTracks && shouldStartTracks) trackState = TrackState.Ready;
// if (!hasTracks && shouldStartTracks) trackState = TrackState.Creating;
const trackState: TrackState = shouldStartTracks
? TrackState.Ready
: TrackState.WaitingForUser;

View File

@@ -34,7 +34,7 @@ beforeEach(() => {
scope = new ObservableScope();
});
// afterEach(() => scope.end());
afterEach(() => scope.end());
function createMockLocalTrack(source: Track.Source): LocalTrack {
const track = {

View File

@@ -97,7 +97,7 @@ export class Publisher {
// it would also prevent the user from seeing their own video/audio preview.
// So for that we use pauseUpStream(): Stops sending media to the server by replacing
// the sender track with null, but keeps the local MediaStreamTrack active.
// The user can still see/hear themselves locally, but remote participants see nothing
// The user can still see/hear themselves locally, but remote participants see nothing.
private onLocalTrackPublished(
localTrackPublication: LocalTrackPublication,
): void {
@@ -128,6 +128,15 @@ export class Publisher {
}
}
/**
* Create and setup local audio and video tracks based on the current mute states.
* It creates the tracks only if audio and/or video is enabled, to avoid unnecessary
* permission prompts.
*
* It also observes mute state changes to update LiveKit microphone/camera states accordingly.
* If a track is not created initially because disabled, it will be created when unmuting.
*
* This call is not blocking anymore, instead callers can listen to the
* `RoomEvent.MediaDevicesError` event in the LiveKit room to be notified of any errors.
*
*/
public async createAndSetupTracks(): Promise<void> {
@@ -141,25 +150,21 @@ export class Publisher {
const audio = this.muteStates.audio.enabled$.value;
const video = this.muteStates.video.enabled$.value;
const enableTracks = async (): Promise<void> => {
if (audio && video) {
// Enable both at once in order to have a single permission prompt!
await lkRoom.localParticipant.enableCameraAndMicrophone();
} else if (audio) {
await lkRoom.localParticipant.setMicrophoneEnabled(true);
} else if (video) {
await lkRoom.localParticipant.setCameraEnabled(true);
}
return;
};
// We don't await enableTracks, because livekit could block until the tracks
// We don't await the creation, because livekit could block until the tracks
// are fully published, and not only that they are created.
// We don't have control on that, localParticipant creates and publishes the tracks
// asap.
// We are using the `ParticipantEvent.LocalTrackPublished` to be notified
// when tracks are actually published, and at that point
// we can pause upstream if needed (depending on if startPublishing has been called).
void enableTracks();
if (audio && video) {
// Enable both at once in order to have a single permission prompt!
void lkRoom.localParticipant.enableCameraAndMicrophone();
} else if (audio) {
void lkRoom.localParticipant.setMicrophoneEnabled(true);
} else if (video) {
void lkRoom.localParticipant.setCameraEnabled(true);
}
return Promise.resolve();
}
@@ -233,6 +238,8 @@ export class Publisher {
public async stopPublishing(): Promise<void> {
this.logger.debug("stopPublishing called");
this.shouldPublish = false;
// Pause upstream will stop sending media to the server, while keeping
// the local MediaStreamTrack active, so the user can still see themselves.
await this.pauseUpstreams(this.connection.livekitRoom, [
Track.Source.Microphone,
Track.Source.Camera,