// Background-blur processor wrapper. LiveKit's `@livekit/track-processors` // ships a MediaPipe-based selfie-segmentation pipeline that keeps the // foreground sharp and blurs the background. The model (~1.5MB) downloads // lazily on first activation, so users who never enable blur don't pay // for it. // // attach/detach hide behind a guard so repeated toggles don't create a // stack of processors — `setProcessor(null)` tears down the WebGL context // and frees the GPU surface. import type { LocalParticipant, LocalVideoTrack } from 'livekit-client'; import { Track } from 'livekit-client'; let cachedProcessor: unknown = null; async function getProcessor(): Promise { if (cachedProcessor) return cachedProcessor; const mod = (await import('@livekit/track-processors')) as { BackgroundBlur?: (radius?: number) => unknown; }; if (!mod.BackgroundBlur) { throw new Error('BackgroundBlur not exported by @livekit/track-processors'); } cachedProcessor = mod.BackgroundBlur(12); return cachedProcessor; } function getCameraTrack(lp: LocalParticipant): LocalVideoTrack | null { const pub = lp.getTrackPublication(Track.Source.Camera); const track = pub?.track; if (!track) return null; return track as LocalVideoTrack; } export async function applyBackgroundBlurToLocal(lp: LocalParticipant): Promise { try { const track = getCameraTrack(lp); if (!track) return; const processor = await getProcessor(); // `setProcessor` is declared on LocalVideoTrack; cast because the // processor type lives in a separate module we don't want to strongly // couple to here. await (track as unknown as { setProcessor: (p: unknown) => Promise; }).setProcessor(processor); } catch (err: unknown) { console.warn('applyBackgroundBlur failed', err); } } export async function removeBackgroundBlurFromLocal(lp: LocalParticipant): Promise { try { const track = getCameraTrack(lp); if (!track) return; const setter = (track as unknown as { setProcessor?: (p: unknown) => Promise; stopProcessor?: () => Promise; }); if (setter.stopProcessor) { await setter.stopProcessor(); } else if (setter.setProcessor) { await setter.setProcessor(null); } } catch (err: unknown) { console.warn('removeBackgroundBlur failed', err); } }