diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0a65c24 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +public/background-images/*.jpg filter=lfs diff=lfs merge=lfs -text diff --git a/lib/CameraSettings.tsx b/lib/CameraSettings.tsx new file mode 100644 index 0000000..a1aeabd --- /dev/null +++ b/lib/CameraSettings.tsx @@ -0,0 +1,170 @@ +import React from 'react'; +import { + MediaDeviceMenu, + TrackToggle, + useLocalParticipant, + VideoTrack, +} from '@livekit/components-react'; +import { BackgroundBlur, VirtualBackground } from '@livekit/track-processors'; +import { isLocalTrack, LocalTrackPublication, Track } from 'livekit-client'; +import Desk from '../public/background-images/samantha-gades-BlIhVfXbi9s-unsplash.jpg'; +import Nature from '../public/background-images/ali-kazal-tbw_KQE3Cbg-unsplash.jpg'; + +// Background image paths +const BACKGROUND_IMAGES = [ + { name: 'Desk', path: Desk }, + { name: 'Nature', path: Nature }, +]; + +// Background options +type BackgroundType = 'none' | 'blur' | 'image'; + +export function CameraSettings() { + const { cameraTrack, localParticipant } = useLocalParticipant(); + const [backgroundType, setBackgroundType] = React.useState( + (cameraTrack as LocalTrackPublication)?.track?.getProcessor()?.name === 'background-blur' + ? 'blur' + : (cameraTrack as LocalTrackPublication)?.track?.getProcessor()?.name === 'virtual-background' + ? 'image' + : 'none', + ); + + const [virtualBackgroundImagePath, setVirtualBackgroundImagePath] = React.useState( + null, + ); + + const camTrackRef = React.useMemo(() => { + return { participant: localParticipant, publication: cameraTrack, source: Track.Source.Camera }; + }, [localParticipant, cameraTrack]); + + const selectBackground = (type: BackgroundType, imagePath?: string) => { + setBackgroundType(type); + if (type === 'image' && imagePath) { + setVirtualBackgroundImagePath(imagePath); + } else if (type !== 'image') { + setVirtualBackgroundImagePath(null); + } + }; + + React.useEffect(() => { + if (isLocalTrack(cameraTrack?.track)) { + if (backgroundType === 'blur') { + cameraTrack.track?.setProcessor(BackgroundBlur()); + } else if (backgroundType === 'image' && virtualBackgroundImagePath) { + cameraTrack.track?.setProcessor(VirtualBackground(virtualBackgroundImagePath)); + } else { + cameraTrack.track?.stopProcessor(); + } + } + }, [cameraTrack, backgroundType, virtualBackgroundImagePath]); + + return ( +
+ + +
+ Camera +
+ +
+
+ +
+
Background Effects
+
+ + + + + {BACKGROUND_IMAGES.map((image) => ( + + ))} +
+
+
+ ); +} diff --git a/lib/MicrophoneSettings.tsx b/lib/MicrophoneSettings.tsx new file mode 100644 index 0000000..7c7a9b9 --- /dev/null +++ b/lib/MicrophoneSettings.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { useKrispNoiseFilter } from '@livekit/components-react/krisp'; +import { TrackToggle } from '@livekit/components-react'; +import { MediaDeviceMenu } from '@livekit/components-react'; +import { Track } from 'livekit-client'; + +export function MicrophoneSettings() { + const { isNoiseFilterEnabled, setNoiseFilterEnabled, isNoiseFilterPending } = + useKrispNoiseFilter(); + + React.useEffect(() => { + // enable Krisp by default + setNoiseFilterEnabled(true); + }, []); + return ( +
+
+ Microphone +
+ +
+
+ + +
+ ); +} diff --git a/lib/SettingsMenu.tsx b/lib/SettingsMenu.tsx index c817e9c..035498a 100644 --- a/lib/SettingsMenu.tsx +++ b/lib/SettingsMenu.tsx @@ -8,9 +8,9 @@ import { useRoomContext, useIsRecording, } from '@livekit/components-react'; -import { useKrispNoiseFilter } from '@livekit/components-react/krisp'; import styles from '../styles/SettingsMenu.module.css'; - +import { CameraSettings } from './CameraSettings'; +import { MicrophoneSettings } from './MicrophoneSettings'; /** * @alpha */ @@ -27,7 +27,6 @@ export function SettingsMenu(props: SettingsMenuProps) { const settings = React.useMemo(() => { return { media: { camera: true, microphone: true, label: 'Media Devices', speaker: true }, - effects: { label: 'Effects' }, recording: recordingEndpoint ? { label: 'Recording' } : undefined, }; }, []); @@ -38,14 +37,6 @@ export function SettingsMenu(props: SettingsMenuProps) { ); const [activeTab, setActiveTab] = React.useState(tabs[0]); - const { isNoiseFilterEnabled, setNoiseFilterEnabled, isNoiseFilterPending } = - useKrispNoiseFilter(); - - React.useEffect(() => { - // enable Krisp by default - setNoiseFilterEnabled(true); - }, []); - const isRecording = useIsRecording(); const [initialRecStatus, setInitialRecStatus] = React.useState(isRecording); const [processingRecRequest, setProcessingRecRequest] = React.useState(false); @@ -108,22 +99,16 @@ export function SettingsMenu(props: SettingsMenuProps) { {settings.media && settings.media.camera && ( <>

Camera

-
- Camera -
- -
+
+
)} {settings.media && settings.media.microphone && ( <>

Microphone

-
- Microphone -
- -
+
+
)} @@ -140,21 +125,6 @@ export function SettingsMenu(props: SettingsMenuProps) { )} )} - {activeTab === 'effects' && ( - <> -

Audio

-
- - setNoiseFilterEnabled(ev.target.checked)} - checked={isNoiseFilterEnabled} - disabled={isNoiseFilterPending} - > -
- - )} {activeTab === 'recording' && ( <>

Record Meeting

diff --git a/next.config.js b/next.config.js index 76f094f..cb09092 100644 --- a/next.config.js +++ b/next.config.js @@ -2,6 +2,9 @@ const nextConfig = { reactStrictMode: false, productionBrowserSourceMaps: true, + images: { + formats: ['image/webp'], + }, webpack: (config, { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }) => { // Important: return the modified config config.module.rules.push({ @@ -9,6 +12,7 @@ const nextConfig = { enforce: 'pre', use: ['source-map-loader'], }); + return config; }, }; diff --git a/package.json b/package.json index 845bd33..294dc4e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@livekit/components-react": "2.9.2", "@livekit/components-styles": "1.1.5", "@livekit/krisp-noise-filter": "0.2.16", + "@livekit/track-processors": "^0.5.2", "livekit-client": "2.11.2", "livekit-server-sdk": "2.12.0", "next": "15.2.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d82017..fdcb72e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@livekit/krisp-noise-filter': specifier: 0.2.16 version: 0.2.16(livekit-client@2.11.2) + '@livekit/track-processors': + specifier: ^0.5.2 + version: 0.5.2(livekit-client@2.11.2) livekit-client: specifier: 2.11.2 version: 2.11.2 @@ -315,6 +318,14 @@ packages: '@livekit/protocol@1.36.1': resolution: {integrity: sha512-nN3QnITAQ5yXk7UKfotH7CRWIlEozNWeKVyFJ0/+dtSzvWP/ib+10l1DDnRYi3A1yICJOGAKFgJ5d6kmi1HCUA==} + '@livekit/track-processors@0.5.2': + resolution: {integrity: sha512-hnAD8PyCE3OPOohFYkPCEGGLeY4/oUa1gu3VCJ4sfelvrbZUKi0vEbUhbnnNcP9V7YiYzplktUz2w6EtYVPRLA==} + peerDependencies: + livekit-client: ^1.12.0 || ^2.1.0 + + '@mediapipe/tasks-vision@0.10.22-rc.20250304': + resolution: {integrity: sha512-dElxVXMFGthshfIj+qAVm8KE2jmNo2p8oXFib8WzEjb7GNaX/ClWBc8UJfoSZwjEMVrdHJ4YUfa7P3ifl6MIWw==} + '@next/env@15.2.4': resolution: {integrity: sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==} @@ -2043,6 +2054,13 @@ snapshots: dependencies: '@bufbuild/protobuf': 1.10.0 + '@livekit/track-processors@0.5.2(livekit-client@2.11.2)': + dependencies: + '@mediapipe/tasks-vision': 0.10.22-rc.20250304 + livekit-client: 2.11.2 + + '@mediapipe/tasks-vision@0.10.22-rc.20250304': {} + '@next/env@15.2.4': {} '@next/eslint-plugin-next@15.2.4': diff --git a/public/background-images/ali-kazal-tbw_KQE3Cbg-unsplash.jpg b/public/background-images/ali-kazal-tbw_KQE3Cbg-unsplash.jpg new file mode 100644 index 0000000..1d133f3 --- /dev/null +++ b/public/background-images/ali-kazal-tbw_KQE3Cbg-unsplash.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a3c9eb8da1ef3ddf2439428b49c11abd9a765e056600bd4f1d89a5dfc82778a +size 52339 diff --git a/public/background-images/samantha-gades-BlIhVfXbi9s-unsplash.jpg b/public/background-images/samantha-gades-BlIhVfXbi9s-unsplash.jpg new file mode 100644 index 0000000..94add64 --- /dev/null +++ b/public/background-images/samantha-gades-BlIhVfXbi9s-unsplash.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bc017736e04acb0188f69cec0aafb88bd6891bfc4a6ff1530665e8dc210dbdf +size 1273171