Make the background effects state an interface built by a function

- BackgroundEffects is an interface, built by createBackgroundEffects,
  following the convention for state holders, as review asked.
- Same behaviour: the body moves from the constructor unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
fkwp
2026-10-08 12:22:34 +02:00
co-authored by Claude Opus 5.5
parent e9365b85b5
commit 32e7c407c3
3 changed files with 47 additions and 40 deletions
+5 -2
View File
@@ -28,7 +28,10 @@ import { OneStepPipeline } from "./OneStepPipeline";
import { supportsBackgroundProcessors } from "./backgroundProcessing"; import { supportsBackgroundProcessors } from "./backgroundProcessing";
import { type Behavior } from "../state/Behavior"; import { type Behavior } from "../state/Behavior";
import { ObservableScope } from "../state/ObservableScope"; import { ObservableScope } from "../state/ObservableScope";
import { BackgroundEffects } from "../state/BackgroundEffects"; import {
type BackgroundEffects,
createBackgroundEffects,
} from "../state/BackgroundEffects";
import { useBehavior } from "../useBehavior"; import { useBehavior } from "../useBehavior";
//TODO-MULTI-SFU: This is not yet fully there. //TODO-MULTI-SFU: This is not yet fully there.
@@ -124,7 +127,7 @@ export const BackgroundEffectsProvider: FC<Props> = ({ children }) => {
useEffect(() => { useEffect(() => {
const scope = new ObservableScope(); const scope = new ObservableScope();
setEffects( setEffects(
new BackgroundEffects(scope, { createBackgroundEffects(scope, {
supported: supportsBackgroundProcessors(), supported: supportsBackgroundProcessors(),
blur$: backgroundBlurSettings.value$, blur$: backgroundBlurSettings.value$,
pipeline: new OneStepPipeline( pipeline: new OneStepPipeline(
+4 -3
View File
@@ -10,7 +10,8 @@ import { BehaviorSubject, distinctUntilChanged, map } from "rxjs";
import { type BackgroundProcessorWrapper } from "@livekit/track-processors"; import { type BackgroundProcessorWrapper } from "@livekit/track-processors";
import { import {
BackgroundEffects, type BackgroundEffects,
createBackgroundEffects,
type BackgroundEffectsOptions, type BackgroundEffectsOptions,
} from "./BackgroundEffects"; } from "./BackgroundEffects";
import { type BackgroundEffectsState } from "../livekit/BackgroundEffectsContext"; import { type BackgroundEffectsState } from "../livekit/BackgroundEffectsContext";
@@ -58,7 +59,7 @@ describe("the pipeline's state", () => {
expected: string; expected: string;
}): void { }): void {
withTestScheduler(({ behavior, expectObservable }) => { withTestScheduler(({ behavior, expectObservable }) => {
const effects = new BackgroundEffects(testScope(), { const effects = createBackgroundEffects(testScope(), {
supported: true, supported: true,
blur$: behavior(effect, { n: false, b: true }), blur$: behavior(effect, { n: false, b: true }),
pipeline: fakePipeline().pipeline, pipeline: fakePipeline().pipeline,
@@ -80,7 +81,7 @@ describe("background effects", () => {
function build( function build(
options: Partial<BackgroundEffectsOptions> = {}, options: Partial<BackgroundEffectsOptions> = {},
): BackgroundEffects { ): BackgroundEffects {
return new BackgroundEffects(testScope(), { return createBackgroundEffects(testScope(), {
supported: true, supported: true,
blur$, blur$,
pipeline: fake.pipeline, pipeline: fake.pipeline,
+38 -35
View File
@@ -29,44 +29,47 @@ export interface BackgroundEffectsOptions {
} }
/** The background effect pipeline, as the camera tracks and the menus see it. */ /** The background effect pipeline, as the camera tracks and the menus see it. */
export class BackgroundEffects { export interface BackgroundEffects {
public readonly state$: Behavior<BackgroundEffectsState>; readonly state$: Behavior<BackgroundEffectsState>;
}
public constructor( /** Switches the pipeline as the choice changes, for as long as the scope lasts. */
scope: ObservableScope, export function createBackgroundEffects(
{ supported, blur$, pipeline }: BackgroundEffectsOptions, scope: ObservableScope,
) { { supported, blur$, pipeline }: BackgroundEffectsOptions,
this.state$ = scope.behavior( ): BackgroundEffects {
blur$.pipe( const state$ = scope.behavior(
scan<boolean, BackgroundEffectsState>( blur$.pipe(
(previous, wanted) => { scan<boolean, BackgroundEffectsState>(
// Attached the first time an effect is wanted and never detached (previous, wanted) => {
// after, so someone who never turns one on pays for none of it. // Attached the first time an effect is wanted and never detached
const enable = // after, so someone who never turns one on pays for none of it.
previous.processor !== undefined || (supported && wanted); const enable =
return { supported, processor: enable ? pipeline : undefined }; previous.processor !== undefined || (supported && wanted);
}, return { supported, processor: enable ? pipeline : undefined };
{ supported, processor: undefined }, },
), { supported, processor: undefined },
), ),
); ),
);
const switchTo = oneSwitchAtATime(pipeline); const switchTo = oneSwitchAtATime(pipeline);
combineLatest([this.state$, blur$]) combineLatest([state$, blur$])
.pipe( .pipe(
filter(([{ processor }]) => processor !== undefined), filter(([{ processor }]) => processor !== undefined),
map(([, blur]): SwitchBackgroundProcessorOptions => map(([, blur]): SwitchBackgroundProcessorOptions =>
blur ? { mode: "background-blur", blurRadius } : { mode: "disabled" }, blur ? { mode: "background-blur", blurRadius } : { mode: "disabled" },
), ),
distinctUntilChanged(deepCompare), distinctUntilChanged(deepCompare),
scope.bind(), scope.bind(),
) )
.subscribe((options) => { .subscribe((options) => {
switchTo(options).catch((e) => switchTo(options).catch((e) =>
logger.warn("Failed to switch background effect", e), logger.warn("Failed to switch background effect", e),
); );
}); });
}
return { state$ };
} }
/** /**