diff --git a/.changeset/eight-turtles-tell.md b/.changeset/eight-turtles-tell.md new file mode 100644 index 000000000..f6104c88d --- /dev/null +++ b/.changeset/eight-turtles-tell.md @@ -0,0 +1,5 @@ +--- +"@livekit/components-react": patch +--- + +Make processor initialization work on track create diff --git a/examples/nextjs/pages/e2ee.tsx b/examples/nextjs/pages/e2ee.tsx index 7ce78531d..3d88c3472 100644 --- a/examples/nextjs/pages/e2ee.tsx +++ b/examples/nextjs/pages/e2ee.tsx @@ -9,7 +9,7 @@ import { generateRandomUserId } from '../lib/helper'; const E2EEExample: NextPage = () => { const params = typeof window !== 'undefined' ? new URLSearchParams(location.search) : null; const roomName = params?.get('room') ?? 'test-room'; - const userIdentity = params?.get('user') ?? generateRandomUserId(); + const userIdentity = React.useMemo(() => params?.get('user') ?? generateRandomUserId(), []); setLogLevel('warn', { liveKitClientLogLevel: 'debug' }); const token = useToken(process.env.NEXT_PUBLIC_LK_TOKEN_ENDPOINT, roomName, { diff --git a/examples/nextjs/pages/prejoin.tsx b/examples/nextjs/pages/prejoin.tsx index d0d4ec59f..884b873ae 100644 --- a/examples/nextjs/pages/prejoin.tsx +++ b/examples/nextjs/pages/prejoin.tsx @@ -3,13 +3,24 @@ import * as React from 'react'; import { PreJoin, setLogLevel } from '@livekit/components-react'; import type { NextPage } from 'next'; +import { Track, TrackProcessor } from 'livekit-client'; +import { BackgroundBlur } from '@livekit/track-processors'; const PreJoinExample: NextPage = () => { setLogLevel('debug', { liveKitClientLogLevel: 'warn' }); + const [backgroundBlur, setBackgroundBlur] = React.useState< + TrackProcessor | undefined + >(undefined); + + React.useEffect(() => { + setBackgroundBlur(BackgroundBlur()); + }, []); + return (
{ values.audioDeviceId; diff --git a/examples/nextjs/pages/processors.tsx b/examples/nextjs/pages/processors.tsx index 64f570033..44f3712ce 100644 --- a/examples/nextjs/pages/processors.tsx +++ b/examples/nextjs/pages/processors.tsx @@ -13,7 +13,7 @@ import { } from '@livekit/components-react'; import type { NextPage } from 'next'; import { ControlBarControls } from '@livekit/components-react'; -import { LocalVideoTrack, Track } from 'livekit-client'; +import { LocalVideoTrack, Track, TrackProcessor } from 'livekit-client'; import { BackgroundBlur } from '@livekit/track-processors'; function Stage() { @@ -23,6 +23,7 @@ function Stage() { const [blurEnabled, setBlurEnabled] = React.useState(false); const [processorPending, setProcessorPending] = React.useState(false); const { cameraTrack } = useLocalParticipant(); + const [blur] = React.useState(BackgroundBlur()); React.useEffect(() => { const localCamTrack = cameraTrack?.track as LocalVideoTrack | undefined; @@ -30,7 +31,7 @@ function Stage() { setProcessorPending(true); try { if (blurEnabled && !localCamTrack.getProcessor()) { - localCamTrack.setProcessor(BackgroundBlur()); + localCamTrack.setProcessor(blur); } else if (!blurEnabled) { localCamTrack.stopProcessor(); } diff --git a/packages/react/etc/components-react.api.md b/packages/react/etc/components-react.api.md index 01c37d8a5..c27495bea 100644 --- a/packages/react/etc/components-react.api.md +++ b/packages/react/etc/components-react.api.md @@ -34,6 +34,7 @@ import { ScreenShareCaptureOptions } from 'livekit-client'; import { setLogLevel as setLogLevel_2 } from 'livekit-client'; import { SVGProps } from 'react'; import { Track } from 'livekit-client'; +import { TrackProcessor } from 'livekit-client'; import { TrackPublication } from 'livekit-client'; import { TrackPublishOptions } from 'livekit-client'; import { TranscriptionSegment } from 'livekit-client'; @@ -546,7 +547,7 @@ export interface ParticipantTileProps extends React_2.HTMLAttributes, 'onSubmit' | 'onError'> { @@ -566,6 +567,8 @@ export interface PreJoinProps extends Omit; } // Warning: (ae-internal-missing-underscore) The name "QualityExcellentIcon" should be prefixed with an underscore because the declaration is marked as @internal diff --git a/packages/react/src/hooks/useLiveKitRoom.ts b/packages/react/src/hooks/useLiveKitRoom.ts index 680ea4f83..bbcef09be 100644 --- a/packages/react/src/hooks/useLiveKitRoom.ts +++ b/packages/react/src/hooks/useLiveKitRoom.ts @@ -6,6 +6,7 @@ import type { HTMLAttributes } from 'react'; import type { LiveKitRoomProps } from '../components'; import { mergeProps } from '../mergeProps'; +import { roomOptionsStringifyReplacer } from '../utils'; const defaultRoomProps: Partial = { connect: true, @@ -60,7 +61,7 @@ export function useLiveKitRoom( React.useEffect(() => { setRoom(passedRoom ?? new Room(options)); - }, [passedRoom]); + }, [passedRoom, JSON.stringify(options, roomOptionsStringifyReplacer)]); const htmlProps = React.useMemo(() => { const { className } = setupLiveKitRoom(); diff --git a/packages/react/src/prefabs/PreJoin.tsx b/packages/react/src/prefabs/PreJoin.tsx index 7a1bde3dc..65dd2f34d 100644 --- a/packages/react/src/prefabs/PreJoin.tsx +++ b/packages/react/src/prefabs/PreJoin.tsx @@ -3,6 +3,7 @@ import type { LocalAudioTrack, LocalTrack, LocalVideoTrack, + TrackProcessor, } from 'livekit-client'; import { createLocalAudioTrack, @@ -22,6 +23,7 @@ import { ParticipantPlaceholder } from '../assets/images'; import { useMediaDevices, usePersistentUserChoices } from '../hooks'; import { useWarnAboutMissingStyles } from '../hooks/useWarnAboutMissingStyles'; import { defaultUserChoices } from '@livekit/components-core'; +import { roomOptionsStringifyReplacer } from '../utils'; /** * Props for the PreJoin component. @@ -50,6 +52,7 @@ export interface PreJoinProps * @alpha */ persistUserChoices?: boolean; + videoProcessor?: TrackProcessor; } /** @alpha */ @@ -92,7 +95,7 @@ export function usePreviewTracks( track.stop(); }); }; - }, [JSON.stringify(options), onError, trackLock]); + }, [JSON.stringify(options, roomOptionsStringifyReplacer), onError, trackLock]); return tracks; } @@ -222,6 +225,7 @@ export function PreJoin({ camLabel = 'Camera', userLabel = 'Username', persistUserChoices = true, + videoProcessor, ...htmlProps }: PreJoinProps) { const [userChoices, setUserChoices] = React.useState(defaultUserChoices); @@ -279,7 +283,9 @@ export function PreJoin({ const tracks = usePreviewTracks( { audio: audioEnabled ? { deviceId: initialUserChoices.audioDeviceId } : false, - video: videoEnabled ? { deviceId: initialUserChoices.videoDeviceId } : false, + video: videoEnabled + ? { deviceId: initialUserChoices.videoDeviceId, processor: videoProcessor } + : false, }, onError, ); diff --git a/packages/react/src/utils.ts b/packages/react/src/utils.ts index 5486a6671..8a7fe250c 100644 --- a/packages/react/src/utils.ts +++ b/packages/react/src/utils.ts @@ -60,3 +60,19 @@ export function warnAboutMissingStyles(el?: HTMLElement) { } } } + +/** + * + * @internal + * used to stringify room options to detect dependency changes for react hooks. + * Replaces processors and e2ee options with strings. + */ +export function roomOptionsStringifyReplacer(key: string, val: unknown) { + if (key === 'processor' && val && typeof val === 'object' && 'name' in val) { + return val.name; + } + if (key === 'e2ee' && val) { + return 'e2ee-enabled'; + } + return val; +}