diff --git a/src/livekit/BackgroundEffectsContext.tsx b/src/livekit/BackgroundEffectsContext.tsx index af3ca41f2..b9a0a4b98 100644 --- a/src/livekit/BackgroundEffectsContext.tsx +++ b/src/livekit/BackgroundEffectsContext.tsx @@ -28,7 +28,10 @@ import { OneStepPipeline } from "./OneStepPipeline"; import { supportsBackgroundProcessors } from "./backgroundProcessing"; import { type Behavior } from "../state/Behavior"; import { ObservableScope } from "../state/ObservableScope"; -import { BackgroundEffects } from "../state/BackgroundEffects"; +import { + type BackgroundEffects, + createBackgroundEffects, +} from "../state/BackgroundEffects"; import { useBehavior } from "../useBehavior"; //TODO-MULTI-SFU: This is not yet fully there. @@ -124,7 +127,7 @@ export const BackgroundEffectsProvider: FC = ({ children }) => { useEffect(() => { const scope = new ObservableScope(); setEffects( - new BackgroundEffects(scope, { + createBackgroundEffects(scope, { supported: supportsBackgroundProcessors(), blur$: backgroundBlurSettings.value$, pipeline: new OneStepPipeline( diff --git a/src/state/BackgroundEffects.test.ts b/src/state/BackgroundEffects.test.ts index 6901c0be5..a57ad5161 100644 --- a/src/state/BackgroundEffects.test.ts +++ b/src/state/BackgroundEffects.test.ts @@ -10,7 +10,8 @@ import { BehaviorSubject, distinctUntilChanged, map } from "rxjs"; import { type BackgroundProcessorWrapper } from "@livekit/track-processors"; import { - BackgroundEffects, + type BackgroundEffects, + createBackgroundEffects, type BackgroundEffectsOptions, } from "./BackgroundEffects"; import { type BackgroundEffectsState } from "../livekit/BackgroundEffectsContext"; @@ -58,7 +59,7 @@ describe("the pipeline's state", () => { expected: string; }): void { withTestScheduler(({ behavior, expectObservable }) => { - const effects = new BackgroundEffects(testScope(), { + const effects = createBackgroundEffects(testScope(), { supported: true, blur$: behavior(effect, { n: false, b: true }), pipeline: fakePipeline().pipeline, @@ -80,7 +81,7 @@ describe("background effects", () => { function build( options: Partial = {}, ): BackgroundEffects { - return new BackgroundEffects(testScope(), { + return createBackgroundEffects(testScope(), { supported: true, blur$, pipeline: fake.pipeline, diff --git a/src/state/BackgroundEffects.ts b/src/state/BackgroundEffects.ts index 51e4b081b..58bcf2b73 100644 --- a/src/state/BackgroundEffects.ts +++ b/src/state/BackgroundEffects.ts @@ -29,44 +29,47 @@ export interface BackgroundEffectsOptions { } /** The background effect pipeline, as the camera tracks and the menus see it. */ -export class BackgroundEffects { - public readonly state$: Behavior; +export interface BackgroundEffects { + readonly state$: Behavior; +} - public constructor( - scope: ObservableScope, - { supported, blur$, pipeline }: BackgroundEffectsOptions, - ) { - this.state$ = scope.behavior( - blur$.pipe( - scan( - (previous, wanted) => { - // Attached the first time an effect is wanted and never detached - // after, so someone who never turns one on pays for none of it. - const enable = - previous.processor !== undefined || (supported && wanted); - return { supported, processor: enable ? pipeline : undefined }; - }, - { supported, processor: undefined }, - ), +/** Switches the pipeline as the choice changes, for as long as the scope lasts. */ +export function createBackgroundEffects( + scope: ObservableScope, + { supported, blur$, pipeline }: BackgroundEffectsOptions, +): BackgroundEffects { + const state$ = scope.behavior( + blur$.pipe( + scan( + (previous, wanted) => { + // Attached the first time an effect is wanted and never detached + // after, so someone who never turns one on pays for none of it. + const enable = + previous.processor !== undefined || (supported && wanted); + return { supported, processor: enable ? pipeline : undefined }; + }, + { supported, processor: undefined }, ), - ); + ), + ); - const switchTo = oneSwitchAtATime(pipeline); - combineLatest([this.state$, blur$]) - .pipe( - filter(([{ processor }]) => processor !== undefined), - map(([, blur]): SwitchBackgroundProcessorOptions => - blur ? { mode: "background-blur", blurRadius } : { mode: "disabled" }, - ), - distinctUntilChanged(deepCompare), - scope.bind(), - ) - .subscribe((options) => { - switchTo(options).catch((e) => - logger.warn("Failed to switch background effect", e), - ); - }); - } + const switchTo = oneSwitchAtATime(pipeline); + combineLatest([state$, blur$]) + .pipe( + filter(([{ processor }]) => processor !== undefined), + map(([, blur]): SwitchBackgroundProcessorOptions => + blur ? { mode: "background-blur", blurRadius } : { mode: "disabled" }, + ), + distinctUntilChanged(deepCompare), + scope.bind(), + ) + .subscribe((options) => { + switchTo(options).catch((e) => + logger.warn("Failed to switch background effect", e), + ); + }); + + return { state$ }; } /**