diff --git a/src/state/CallViewModel/CallViewModel.test.ts b/src/state/CallViewModel/CallViewModel.test.ts index 4cf7ebfeb..84efc79c7 100644 --- a/src/state/CallViewModel/CallViewModel.test.ts +++ b/src/state/CallViewModel/CallViewModel.test.ts @@ -1697,7 +1697,9 @@ describe("a camera with a video processor", () => { ).toMatchObject({ processor }); }); - it("replaces a camera turned off before the processor was chosen", async () => { + // The SDK builds the processor on the camera's restart, before the room + // sees a frame, so the track is unmuted rather than published anew + it("turns on a camera turned off before the processor was chosen", async () => { const setHandler = vi.spyOn(MuteState.prototype, "setHandler"); onTestFinished(() => setHandler.mockRestore()); const videoProcessor$ = new BehaviorSubject( @@ -1711,31 +1713,6 @@ describe("a camera with a video processor", () => { }, { videoProcessor$ }, ); - // The camera is off, so it is stopped and can't take the processor - videoProcessor$.next(processor); - await cameraHandler(setHandler)(true); - await flushPromises(); - - expect(participation!.unpublish).toHaveBeenCalledWith("camera"); - expect(participation!.publish).toHaveBeenCalledWith( - expect.objectContaining({ source: "camera", processor }), - ); - }); - - it("turns on a camera that took the processor while running", async () => { - const setHandler = vi.spyOn(MuteState.prototype, "setHandler"); - onTestFinished(() => setHandler.mockRestore()); - const videoProcessor$ = new BehaviorSubject( - undefined, - ); - let participation: RTCParticipation | undefined; - withCallViewModel( - { videoEnabled: new Map([[localParticipant, constant(true)]]) }, - (_vm, _session, { rtcParticipation }) => { - participation = rtcParticipation; - }, - { videoProcessor$ }, - ); videoProcessor$.next(processor); await cameraHandler(setHandler)(true); await flushPromises(); diff --git a/src/state/CallViewModel/CallViewModel.ts b/src/state/CallViewModel/CallViewModel.ts index 42e310fe2..9b1e853a3 100644 --- a/src/state/CallViewModel/CallViewModel.ts +++ b/src/state/CallViewModel/CallViewModel.ts @@ -14,7 +14,6 @@ import { type EncryptionSystem, generateItems, type LocalAudioMediaTrack, - type LocalMediaTrack, type LocalRTCMember, type LocalVideoMediaTrack, type RTCParticipation, @@ -513,16 +512,6 @@ export function createCallViewModel$( "screenShare", ) as Behavior; - // Camera tracks the processor reached while they ran. A camera turned off is - // stopped, and a stopped track can't take one. - const tookProcessor = new WeakMap(); - const missesProcessor = (track: LocalMediaTrack): boolean => { - const processor = videoProcessor$.value; - return ( - processor !== undefined && - tookProcessor.get(track as LocalVideoMediaTrack) !== processor - ); - }; const supply = (source: "microphone" | "camera"): TrackSupply => ({ publish: async () => rtcParticipation.publish( @@ -533,16 +522,6 @@ export function createCallViewModel$( source === "camera" ? videoProcessor$.value : undefined, ), ), - // A camera turned off before the processor was chosen is replaced, so - // that the new one starts with it - replace: - source === "camera" - ? async (track): Promise => { - if (!missesProcessor(track)) return false; - await rtcParticipation.unpublish("camera"); - return true; - } - : undefined, }); followMuteState( scope, @@ -582,13 +561,7 @@ export function createCallViewModel$( }); }); - followProcessor( - scope, - camera$, - videoProcessor$, - logger, - (camera, processor) => tookProcessor.set(camera, processor), - ); + followProcessor(scope, camera$, videoProcessor$, logger); const members$ = scope.behavior<(LocalRTCMember | RemoteRTCMember)[]>( combineLatest( diff --git a/src/state/localMediaControls.ts b/src/state/localMediaControls.ts index 61521f988..32efe3a7d 100644 --- a/src/state/localMediaControls.ts +++ b/src/state/localMediaControls.ts @@ -18,14 +18,9 @@ import { combineLatest } from "rxjs"; import { type MediaDevice } from "./MediaDevices"; import { type Handler } from "./MuteStates"; -/** - * How a mute switch gets a track where there is none and, where given, - * whether a track it has should be let go and published anew instead of - * unmuted: resolves true once it is gone. - */ +/** How a mute switch gets a track where there is none. */ export interface TrackSupply { publish: () => Promise; - replace?: (track: LocalMediaTrack) => Promise; } /** @@ -51,9 +46,7 @@ export function followMuteState( muteState.setHandler(async (enabled) => { desired = enabled; const track = track$.value; - const replaced = - enabled && track !== undefined && (await supply.replace?.(track)); - if (track !== undefined && !replaced) { + if (track !== undefined) { desired = await track.setEnabled(enabled); return desired; } @@ -107,15 +100,11 @@ export function followProcessor( camera$: Behavior, videoProcessor$: Behavior, logger: Logger, - /** Told of each camera a processor reaches while it runs. */ - applied?: (camera: LocalVideoMediaTrack, processor: VideoProcessor) => void, ): void { combineLatest([camera$, videoProcessor$]) .pipe(scope.bind()) .subscribe(([camera, processor]) => { - if (camera === undefined) return; - if (processor && !camera.muted$.value) applied?.(camera, processor); - camera.setProcessor(processor).catch((e) => { + camera?.setProcessor(processor).catch((e) => { logger.error("Failed to set the video processor", e); }); });