From c798c79f416b7a00926b36034211e9ac0386c63f Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 20 Nov 2024 17:22:24 +0100 Subject: [PATCH 01/51] Add background Blur --- locales/en/app.json | 6 +++- package.json | 3 ++ src/livekit/useLiveKit.ts | 55 +++++++++++++++++++++++++++++++++- src/room/LobbyView.tsx | 35 ++++++++++++++++++---- src/settings/SettingsModal.tsx | 53 +++++++++++++++++++++++++++----- src/settings/settings.ts | 2 ++ yarn.lock | 22 ++++++++++++-- 7 files changed, 159 insertions(+), 17 deletions(-) diff --git a/locales/en/app.json b/locales/en/app.json index 07dd53e3..07edc5e5 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -150,6 +150,9 @@ "effect_volume_description": "Volume for sound effects such as: joining or leaving a call, and reactions.", "effect_volume_label": "Sound effect volume" }, + "background_blur_header": "Background", + "background_blur_label": "Enable background blurring", + "blur_not_supported_by_browser": "", "developer_settings_label": "Developer Settings", "developer_settings_label_description": "Expose developer settings in the settings window.", "developer_tab_title": "Developer", @@ -172,7 +175,8 @@ "preferences_tab_h4": "Preferences", "preferences_tab_show_hand_raised_timer_description": "Show a timer when a participant raises their hand", "preferences_tab_show_hand_raised_timer_label": "Show hand raise duration", - "speaker_device_selection_label": "Speaker" + "speaker_device_selection_label": "Speaker", + "video_tab_activate_background_blur": "Turn on background blur on your webcam video" }, "star_rating_input_label_one": "{{count}} star", "star_rating_input_label_other": "{{count}} stars", diff --git a/package.json b/package.json index 71e3f9c9..f0705f97 100644 --- a/package.json +++ b/package.json @@ -122,5 +122,8 @@ }, "resolutions": { "strip-ansi": "6.0.1" + }, + "dependencies": { + "@livekit/track-processors": "^0.3.2" } } diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 4645fab7..208ed35d 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -9,7 +9,9 @@ import { ConnectionState, E2EEOptions, ExternalE2EEKeyProvider, + LocalTrackPublication, Room, + RoomEvent, RoomOptions, Track, } from "livekit-client"; @@ -17,6 +19,7 @@ import { useEffect, useMemo, useRef } from "react"; import E2EEWorker from "livekit-client/e2ee-worker?worker"; import { logger } from "matrix-js-sdk/src/logger"; import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession"; +import { BackgroundBlur } from "@livekit/track-processors"; import { defaultLiveKitOptions } from "./options"; import { SFUConfig } from "./openIDSFU"; @@ -26,6 +29,7 @@ import { MediaDevices, useMediaDevices, } from "./MediaDevicesContext"; +import { backgroundBlur as backgroundBlurSettings } from "../settings/settings"; import { ECConnectionState, useECConnectionState, @@ -33,6 +37,7 @@ import { import { MatrixKeyProvider } from "../e2ee/matrixKeyProvider"; import { E2eeType } from "../e2ee/e2eeType"; import { EncryptionSystem } from "../e2ee/sharedKeyManagement"; +import { useSetting } from "../settings/settings"; interface UseLivekitResult { livekitRoom?: Room; @@ -78,13 +83,16 @@ export function useLiveKit( const initialMuteStates = useRef(muteStates); const devices = useMediaDevices(); const initialDevices = useRef(devices); - + // eslint-disable-next-line new-cap + const blur = useMemo(() => BackgroundBlur(15), []); const roomOptions = useMemo( (): RoomOptions => ({ ...defaultLiveKitOptions, videoCaptureDefaults: { ...defaultLiveKitOptions.videoCaptureDefaults, deviceId: initialDevices.current.videoInput.selectedId, + // eslint-disable-next-line new-cap + processor: BackgroundBlur(15), }, audioCaptureDefaults: { ...defaultLiveKitOptions.audioCaptureDefaults, @@ -129,6 +137,51 @@ export function useLiveKit( sfuConfig, ); + const [showBackgroundBlur] = useSetting(backgroundBlurSettings); + const videoTrackPromise = useRef< + undefined | Promise + >(undefined); + + useEffect(() => { + if (!room || videoTrackPromise.current) return; + const update = async (): Promise => { + let publishCallback: undefined | ((track: LocalTrackPublication) => void); + videoTrackPromise.current = new Promise< + LocalTrackPublication | undefined + >((resolve) => { + const videoTrack = Array.from( + room.localParticipant.videoTrackPublications.values(), + ).find((v) => v.source === Track.Source.Camera); + if (videoTrack) { + resolve(videoTrack); + } + publishCallback = (videoTrack: LocalTrackPublication): void => { + if (videoTrack.source === Track.Source.Camera) { + resolve(videoTrack); + } + }; + room.on(RoomEvent.LocalTrackPublished, publishCallback); + }); + + const videoTrack = await videoTrackPromise.current; + + if (publishCallback) + room.off(RoomEvent.LocalTrackPublished, publishCallback); + + if (videoTrack !== undefined) { + if (showBackgroundBlur) { + logger.info("Blur: set blur"); + + void videoTrack.track?.setProcessor(blur); + } else { + void videoTrack.track?.stopProcessor(); + } + } + videoTrackPromise.current = undefined; + }; + void update(); + }, [blur, room, showBackgroundBlur]); + useEffect(() => { // Sync the requested mute states with LiveKit's mute states. We do it this // way around rather than using LiveKit as the source of truth, so that the diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index fd3df0c8..db630d09 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { FC, useCallback, useMemo, useState } from "react"; +import { FC, useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { Button } from "@vector-im/compound-web"; @@ -16,6 +16,7 @@ import { usePreviewTracks } from "@livekit/components-react"; import { LocalVideoTrack, Track } from "livekit-client"; import { useObservable } from "observable-hooks"; import { map } from "rxjs"; +import { BackgroundBlur } from "@livekit/track-processors"; import inCallStyles from "./InCallView.module.css"; import styles from "./LobbyView.module.css"; @@ -32,12 +33,14 @@ import { VideoButton, } from "../button/Button"; import { SettingsModal, defaultSettingsTab } from "../settings/SettingsModal"; +import { backgroundBlur as backgroundBlurSettings } from "../settings/settings"; import { useMediaQuery } from "../useMediaQuery"; import { E2eeType } from "../e2ee/e2eeType"; import { Link } from "../button/Link"; import { useMediaDevices } from "../livekit/MediaDevicesContext"; import { useInitial } from "../useInitial"; -import { useSwitchCamera } from "./useSwitchCamera"; +import { useSwitchCamera as useShowSwitchCamera } from "./useSwitchCamera"; +import { useSetting } from "../settings/settings"; interface Props { client: MatrixClient; @@ -108,6 +111,9 @@ export const LobbyView: FC = ({ muteStates.audio.enabled && { deviceId: devices.audioInput.selectedId }, ); + // eslint-disable-next-line new-cap + const blur = useMemo(() => BackgroundBlur(15), []); + const localTrackOptions = useMemo( () => ({ // The only reason we request audio here is to get the audio permission @@ -119,12 +125,15 @@ export const LobbyView: FC = ({ audio: Object.assign({}, initialAudioOptions), video: muteStates.video.enabled && { deviceId: devices.videoInput.selectedId, + // It should be possible to set a processor here: + // processor: blur, + // This causes a crash currently hence we do the effect below... }, }), [ initialAudioOptions, - devices.videoInput.selectedId, muteStates.video.enabled, + devices.videoInput.selectedId, ], ); @@ -146,7 +155,21 @@ export const LobbyView: FC = ({ [tracks], ); - const switchCamera = useSwitchCamera( + const [showBackgroundBlur] = useSetting(backgroundBlurSettings); + + useEffect(() => { + const updateBlur = async (showBlur: boolean): Promise => { + if (showBlur && !videoTrack?.getProcessor()) { + // eslint-disable-next-line new-cap + await videoTrack?.setProcessor(blur); + } else { + await videoTrack?.stopProcessor(); + } + }; + if (videoTrack) void updateBlur(showBackgroundBlur); + }, [videoTrack, showBackgroundBlur, blur]); + + const showSwitchCamera = useShowSwitchCamera( useObservable( (inputs) => inputs.pipe(map(([video]) => video)), [videoTrack], @@ -208,7 +231,9 @@ export const LobbyView: FC = ({ onClick={onVideoPress} disabled={muteStates.video.setEnabled === null} /> - {switchCamera && } + {showSwitchCamera && ( + + )} {!confineToRoom && } diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 9d7eb5a1..81e5f70e 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -5,10 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { ChangeEvent, FC, useCallback, useState } from "react"; +import { ChangeEvent, FC, ReactNode, useCallback, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import { Root as Form, Text } from "@vector-im/compound-web"; +import { + Root as Form, + Separator, + Text, + Tooltip, +} from "@vector-im/compound-web"; import { Modal } from "../Modal"; import styles from "./SettingsModal.module.css"; @@ -26,6 +31,7 @@ import { useSetting, developerSettingsTab as developerSettingsTabSetting, duplicateTiles as duplicateTilesSetting, + backgroundBlur as backgroundBlurSetting, useOptInAnalytics, soundEffectVolumeSetting, } from "./settings"; @@ -70,6 +76,33 @@ export const SettingsModal: FC = ({ ); const [duplicateTiles, setDuplicateTiles] = useSetting(duplicateTilesSetting); + // Generate a `Checkbox` input to turn blur on or off. + const BlurCheckbox: React.FC = (): ReactNode => { + const [blur, setBlur] = useSetting(backgroundBlurSetting); + return ( + <> +

{t("settings.background_blur_header")}

+ + + setBlur(b.target.checked)} + disabled={isFirefox()} + /> + + + + ); + }; + const optInDescription = ( @@ -124,12 +157,16 @@ export const SettingsModal: FC = ({ key: "video", name: t("common.video"), content: ( -
- - + <> +
+ + + + + ), }; diff --git a/src/settings/settings.ts b/src/settings/settings.ts index b2da1674..6f52a525 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -88,6 +88,8 @@ export const videoInput = new Setting( undefined, ); +export const backgroundBlur = new Setting("background-blur", true); + export const showHandRaisedTimer = new Setting( "hand-raised-show-timer", false, diff --git a/yarn.lock b/yarn.lock index ab40c0c0..1a429fc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1812,6 +1812,14 @@ dependencies: "@bufbuild/protobuf" "^1.10.0" +"@livekit/track-processors@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@livekit/track-processors/-/track-processors-0.3.2.tgz#eaff6a48b556c25e85f5dd2c4daf6dcf1bc3b143" + integrity sha512-4JUCzb7yIKoVsTo8J6FTzLZJHcI6DihfX/pGRDg0SOGaxprcDPrt8jaDBBTsnGBSXHeMxl2ugN+xQjdCWzLKEA== + dependencies: + "@mediapipe/holistic" "0.5.1675471629" + "@mediapipe/tasks-vision" "0.10.9" + "@matrix-org/matrix-sdk-crypto-wasm@^9.0.0": version "9.1.0" resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-9.1.0.tgz#f889653eb4fafaad2a963654d586bd34de62acd5" @@ -1822,6 +1830,16 @@ resolved "https://registry.yarnpkg.com/@matrix-org/olm/-/olm-3.2.15.tgz#55f3c1b70a21bbee3f9195cecd6846b1083451ec" integrity sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q== +"@mediapipe/holistic@0.5.1675471629": + version "0.5.1675471629" + resolved "https://registry.yarnpkg.com/@mediapipe/holistic/-/holistic-0.5.1675471629.tgz#f1127d43161ff27e8889d5d39aaea164f9730980" + integrity sha512-qY+cxtDeSOvVtevrLgnodiwXYaAtPi7dHZtNv/bUCGEjFicAOYtMmrZSqMmbPkTB2+4jLnPF1vgshkAqQRSYAw== + +"@mediapipe/tasks-vision@0.10.9": + version "0.10.9" + resolved "https://registry.yarnpkg.com/@mediapipe/tasks-vision/-/tasks-vision-0.10.9.tgz#fbd669f50ac2e888b2c64c9c9863927c111da02f" + integrity sha512-/gFguyJm1ng4Qr7VVH2vKO+zZcQd8wc3YafUfvBuYFX0Y5+CvrV+VNPEVkl5W/gUZF5KNKNZAiaHPULGPCIjyQ== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -6250,14 +6268,14 @@ matrix-js-sdk@matrix-org/matrix-js-sdk#develop: jwt-decode "^4.0.0" loglevel "^1.7.1" matrix-events-sdk "0.0.1" - matrix-widget-api "^1.10.0" + matrix-widget-api "^1.8.2" oidc-client-ts "^3.0.1" p-retry "4" sdp-transform "^2.14.1" unhomoglyph "^1.0.6" uuid "11" -matrix-widget-api@^1.10.0: +matrix-widget-api@^1.10.0, matrix-widget-api@^1.8.2: version "1.10.0" resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.10.0.tgz#d31ea073a5871a1fb1a511ef900b0c125a37bf55" integrity sha512-rkAJ29briYV7TJnfBVLVSKtpeBrBju15JZFSDP6wj8YdbCu1bdmlplJayQ+vYaw1x4fzI49Q+Nz3E85s46sRDw== From 140fe484d79bfdedd9f0068d65866a7561a0b4e4 Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 20 Nov 2024 17:31:40 +0100 Subject: [PATCH 02/51] make compatible with unsopported browsers --- src/livekit/useLiveKit.ts | 18 ++++++++++++++---- src/room/LobbyView.tsx | 13 ++++++++++++- src/settings/SettingsModal.tsx | 17 +++++++++++++++-- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 208ed35d..f99fbabd 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -83,8 +83,16 @@ export function useLiveKit( const initialMuteStates = useRef(muteStates); const devices = useMediaDevices(); const initialDevices = useRef(devices); - // eslint-disable-next-line new-cap - const blur = useMemo(() => BackgroundBlur(15), []); + const blur = useMemo(() => { + let b = undefined; + try { + // eslint-disable-next-line new-cap + b = BackgroundBlur(15); + } catch (e) { + logger.error("disable background blur", e); + } + return b; + }, []); const roomOptions = useMemo( (): RoomOptions => ({ ...defaultLiveKitOptions, @@ -92,7 +100,7 @@ export function useLiveKit( ...defaultLiveKitOptions.videoCaptureDefaults, deviceId: initialDevices.current.videoInput.selectedId, // eslint-disable-next-line new-cap - processor: BackgroundBlur(15), + processor: blur, }, audioCaptureDefaults: { ...defaultLiveKitOptions.audioCaptureDefaults, @@ -103,7 +111,7 @@ export function useLiveKit( }, e2ee: e2eeOptions, }), - [e2eeOptions], + [blur, e2eeOptions], ); // Store if audio/video are currently updating. If to prohibit unnecessary calls @@ -143,6 +151,8 @@ export function useLiveKit( >(undefined); useEffect(() => { + // Fon't even try if we cannot blur on this platform + if (!blur) return; if (!room || videoTrackPromise.current) return; const update = async (): Promise => { let publishCallback: undefined | ((track: LocalTrackPublication) => void); diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index db630d09..6b5db196 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -112,7 +112,16 @@ export const LobbyView: FC = ({ ); // eslint-disable-next-line new-cap - const blur = useMemo(() => BackgroundBlur(15), []); + const blur = useMemo(() => { + let b = undefined; + try { + // eslint-disable-next-line new-cap + b = BackgroundBlur(15); + } catch (e) { + logger.error("disable background blur", e); + } + return b; + }, []); const localTrackOptions = useMemo( () => ({ @@ -158,6 +167,8 @@ export const LobbyView: FC = ({ const [showBackgroundBlur] = useSetting(backgroundBlurSettings); useEffect(() => { + // Fon't even try if we cannot blur on this platform + if (!blur) return; const updateBlur = async (showBlur: boolean): Promise => { if (showBlur && !videoTrack?.getProcessor()) { // eslint-disable-next-line new-cap diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 81e5f70e..2fd19c38 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -14,6 +14,8 @@ import { Text, Tooltip, } from "@vector-im/compound-web"; +import { BackgroundBlur } from "@livekit/track-processors"; +import { logger } from "matrix-js-sdk/src/logger"; import { Modal } from "../Modal"; import styles from "./SettingsModal.module.css"; @@ -79,7 +81,18 @@ export const SettingsModal: FC = ({ // Generate a `Checkbox` input to turn blur on or off. const BlurCheckbox: React.FC = (): ReactNode => { const [blur, setBlur] = useSetting(backgroundBlurSetting); - return ( + let canBlur = true; + try { + // eslint-disable-next-line new-cap + BackgroundBlur(15); + } catch (e) { + logger.debug( + "Cannot blur, so we do not show the option in settings. error: ", + e, + ); + canBlur = false; + } + return canBlur ? ( <>

{t("settings.background_blur_header")}

@@ -100,7 +113,7 @@ export const SettingsModal: FC = ({ - ); + ) : null; }; const optInDescription = ( From 70ff435a8cedb650c5fc14d5d961cebc9ebd214a Mon Sep 17 00:00:00 2001 From: Timo Date: Thu, 21 Nov 2024 17:28:11 +0100 Subject: [PATCH 03/51] add types --- package.json | 2 ++ yarn.lock | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/package.json b/package.json index f0705f97..6e69dd28 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,8 @@ "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^14.5.1", "@types/content-type": "^1.1.5", + "@types/dom-mediacapture-transform": "^0.1.10", + "@types/dom-webcodecs": "^0.1.13", "@types/grecaptcha": "^3.0.9", "@types/jsdom": "^21.1.7", "@types/lodash-es": "^4.17.12", diff --git a/yarn.lock b/yarn.lock index 1a429fc8..9b4f1335 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2999,6 +2999,18 @@ resolved "https://registry.yarnpkg.com/@types/content-type/-/content-type-1.1.8.tgz#319644d07ee6b4bfc734483008393b89b99f0219" integrity sha512-1tBhmVUeso3+ahfyaKluXe38p+94lovUZdoVfQ3OnJo9uJC42JT7CBoN3k9HYhAae+GwiBYmHu+N9FZhOG+2Pg== +"@types/dom-mediacapture-transform@^0.1.10": + version "0.1.10" + resolved "https://registry.yarnpkg.com/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.10.tgz#93e2c04284c95dd2faa2992216417599ee9d27a6" + integrity sha512-zUxMN2iShu7p3Fz5sqfvLp93qW/3sLs+RwXWWOkMb969hsuoVqUUokqrENjXqTMNmEEcVXKoHuMMbIGcWyrVVA== + dependencies: + "@types/dom-webcodecs" "*" + +"@types/dom-webcodecs@*", "@types/dom-webcodecs@^0.1.13": + version "0.1.13" + resolved "https://registry.yarnpkg.com/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz#d8be5da4f01b20721307b08ad2cca903ccf4f47f" + integrity sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ== + "@types/estree@1.0.6", "@types/estree@^1.0.0": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" From d23beefb6091ee1bc96c3776313ea7196449002f Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 27 Nov 2024 18:18:07 +0100 Subject: [PATCH 04/51] remove unused dependencies. --- package.json | 2 -- yarn.lock | 16 ++-------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 6e69dd28..f0705f97 100644 --- a/package.json +++ b/package.json @@ -50,8 +50,6 @@ "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^14.5.1", "@types/content-type": "^1.1.5", - "@types/dom-mediacapture-transform": "^0.1.10", - "@types/dom-webcodecs": "^0.1.13", "@types/grecaptcha": "^3.0.9", "@types/jsdom": "^21.1.7", "@types/lodash-es": "^4.17.12", diff --git a/yarn.lock b/yarn.lock index 9b4f1335..4b5325c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2999,18 +2999,6 @@ resolved "https://registry.yarnpkg.com/@types/content-type/-/content-type-1.1.8.tgz#319644d07ee6b4bfc734483008393b89b99f0219" integrity sha512-1tBhmVUeso3+ahfyaKluXe38p+94lovUZdoVfQ3OnJo9uJC42JT7CBoN3k9HYhAae+GwiBYmHu+N9FZhOG+2Pg== -"@types/dom-mediacapture-transform@^0.1.10": - version "0.1.10" - resolved "https://registry.yarnpkg.com/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.10.tgz#93e2c04284c95dd2faa2992216417599ee9d27a6" - integrity sha512-zUxMN2iShu7p3Fz5sqfvLp93qW/3sLs+RwXWWOkMb969hsuoVqUUokqrENjXqTMNmEEcVXKoHuMMbIGcWyrVVA== - dependencies: - "@types/dom-webcodecs" "*" - -"@types/dom-webcodecs@*", "@types/dom-webcodecs@^0.1.13": - version "0.1.13" - resolved "https://registry.yarnpkg.com/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz#d8be5da4f01b20721307b08ad2cca903ccf4f47f" - integrity sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ== - "@types/estree@1.0.6", "@types/estree@^1.0.0": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" @@ -6280,14 +6268,14 @@ matrix-js-sdk@matrix-org/matrix-js-sdk#develop: jwt-decode "^4.0.0" loglevel "^1.7.1" matrix-events-sdk "0.0.1" - matrix-widget-api "^1.8.2" + matrix-widget-api "^1.10.0" oidc-client-ts "^3.0.1" p-retry "4" sdp-transform "^2.14.1" unhomoglyph "^1.0.6" uuid "11" -matrix-widget-api@^1.10.0, matrix-widget-api@^1.8.2: +matrix-widget-api@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.10.0.tgz#d31ea073a5871a1fb1a511ef900b0c125a37bf55" integrity sha512-rkAJ29briYV7TJnfBVLVSKtpeBrBju15JZFSDP6wj8YdbCu1bdmlplJayQ+vYaw1x4fzI49Q+Nz3E85s46sRDw== From 95bc7db28ac38f59a3892e4a013fae4b351994a3 Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 27 Nov 2024 18:18:50 +0100 Subject: [PATCH 05/51] update blur checkbox (disabled state) --- locales/en/app.json | 2 +- src/settings/SettingsModal.tsx | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/locales/en/app.json b/locales/en/app.json index 07edc5e5..3c2647bd 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -152,7 +152,7 @@ }, "background_blur_header": "Background", "background_blur_label": "Enable background blurring", - "blur_not_supported_by_browser": "", + "blur_not_supported_by_browser": "(Background blur is not supported by this browser.)", "developer_settings_label": "Developer Settings", "developer_settings_label_description": "Expose developer settings in the settings window.", "developer_tab_title": "Developer", diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 2fd19c38..41f66e8d 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -91,29 +91,27 @@ export const SettingsModal: FC = ({ e, ); canBlur = false; + setBlur(false); } - return canBlur ? ( + return ( <>

{t("settings.background_blur_header")}

+ - - setBlur(b.target.checked)} - disabled={isFirefox()} - /> - + type="checkbox" + checked={blur} + onChange={(b): void => setBlur(b.target.checked)} + disabled={!canBlur} + /> - ) : null; + ); }; const optInDescription = ( From 0be312a5e3f421277480f561fe7b6cc506c1015d Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 27 Nov 2024 18:25:37 +0100 Subject: [PATCH 06/51] i18n --- locales/en/app.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/locales/en/app.json b/locales/en/app.json index 3c2647bd..2b610560 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -175,8 +175,7 @@ "preferences_tab_h4": "Preferences", "preferences_tab_show_hand_raised_timer_description": "Show a timer when a participant raises their hand", "preferences_tab_show_hand_raised_timer_label": "Show hand raise duration", - "speaker_device_selection_label": "Speaker", - "video_tab_activate_background_blur": "Turn on background blur on your webcam video" + "speaker_device_selection_label": "Speaker" }, "star_rating_input_label_one": "{{count}} star", "star_rating_input_label_other": "{{count}} stars", From aade8f94a651c34cead3105bc16f9bfd816a9163 Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 27 Nov 2024 18:34:33 +0100 Subject: [PATCH 07/51] eslint + prettier --- src/settings/SettingsModal.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 41f66e8d..84583b8f 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -8,12 +8,7 @@ Please see LICENSE in the repository root for full details. import { ChangeEvent, FC, ReactNode, useCallback, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import { - Root as Form, - Separator, - Text, - Tooltip, -} from "@vector-im/compound-web"; +import { Root as Form, Separator, Text } from "@vector-im/compound-web"; import { BackgroundBlur } from "@livekit/track-processors"; import { logger } from "matrix-js-sdk/src/logger"; From ae71c6e679463ec5c8eb5efdf50987e30fc5301d Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 27 Nov 2024 20:05:35 +0100 Subject: [PATCH 08/51] add @types/dom-mediacapture-transform for the remaining type errors --- package.json | 3 ++- yarn.lock | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f0705f97..ede9bab3 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,7 @@ "strip-ansi": "6.0.1" }, "dependencies": { - "@livekit/track-processors": "^0.3.2" + "@livekit/track-processors": "^0.3.2", + "@types/dom-mediacapture-transform": "^0.1.10" } } diff --git a/yarn.lock b/yarn.lock index 4b5325c3..1a456433 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2999,6 +2999,18 @@ resolved "https://registry.yarnpkg.com/@types/content-type/-/content-type-1.1.8.tgz#319644d07ee6b4bfc734483008393b89b99f0219" integrity sha512-1tBhmVUeso3+ahfyaKluXe38p+94lovUZdoVfQ3OnJo9uJC42JT7CBoN3k9HYhAae+GwiBYmHu+N9FZhOG+2Pg== +"@types/dom-mediacapture-transform@^0.1.10": + version "0.1.10" + resolved "https://registry.yarnpkg.com/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.10.tgz#93e2c04284c95dd2faa2992216417599ee9d27a6" + integrity sha512-zUxMN2iShu7p3Fz5sqfvLp93qW/3sLs+RwXWWOkMb969hsuoVqUUokqrENjXqTMNmEEcVXKoHuMMbIGcWyrVVA== + dependencies: + "@types/dom-webcodecs" "*" + +"@types/dom-webcodecs@*": + version "0.1.13" + resolved "https://registry.yarnpkg.com/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz#d8be5da4f01b20721307b08ad2cca903ccf4f47f" + integrity sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ== + "@types/estree@1.0.6", "@types/estree@^1.0.0": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" From a6745ef34a6380cf9515dd8c46b1b9e3f08b08c6 Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 27 Nov 2024 20:15:37 +0100 Subject: [PATCH 09/51] properly add @livekit/track-processors --- knip.ts | 4 ++++ package.json | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/knip.ts b/knip.ts index becafc2e..5ffe03c4 100644 --- a/knip.ts +++ b/knip.ts @@ -24,6 +24,10 @@ export default { // then Knip will flag it as a false positive // https://github.com/webpro-nl/knip/issues/766 "@vector-im/compound-web", + // We need this so the eslint is happy with @livekit/track-processors. + // This might be a bug in the livekit repo but for now we fix it on the + // element call side. + "@types/dom-mediacapture-transform", "matrix-widget-api", ], ignoreExportsUsedInFile: true, diff --git a/package.json b/package.json index ede9bab3..36e03468 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@formatjs/intl-segmenter": "^11.7.3", "@livekit/components-core": "^0.11.0", "@livekit/components-react": "^2.0.0", + "@livekit/track-processors": "^0.3.2", "@opentelemetry/api": "^1.4.0", "@opentelemetry/core": "^1.25.1", "@opentelemetry/exporter-trace-otlp-http": "^0.55.0", @@ -50,6 +51,7 @@ "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^14.5.1", "@types/content-type": "^1.1.5", + "@types/dom-mediacapture-transform": "^0.1.10", "@types/grecaptcha": "^3.0.9", "@types/jsdom": "^21.1.7", "@types/lodash-es": "^4.17.12", @@ -122,9 +124,5 @@ }, "resolutions": { "strip-ansi": "6.0.1" - }, - "dependencies": { - "@livekit/track-processors": "^0.3.2", - "@types/dom-mediacapture-transform": "^0.1.10" } } From c2b651802745bb237f251b1a5cb61c94b6d469cc Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 27 Nov 2024 20:39:45 +0100 Subject: [PATCH 10/51] dont update if processor already set --- src/livekit/useLiveKit.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index f99fbabd..6e563f1c 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -151,7 +151,7 @@ export function useLiveKit( >(undefined); useEffect(() => { - // Fon't even try if we cannot blur on this platform + // Don't even try if we cannot blur on this platform if (!blur) return; if (!room || videoTrackPromise.current) return; const update = async (): Promise => { @@ -178,7 +178,10 @@ export function useLiveKit( if (publishCallback) room.off(RoomEvent.LocalTrackPublished, publishCallback); - if (videoTrack !== undefined) { + if ( + videoTrack !== undefined && + videoTrack.track?.getProcessor() === undefined + ) { if (showBackgroundBlur) { logger.info("Blur: set blur"); From 7d8e9a79695a876d7549d36b47b6e1490584c891 Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 27 Nov 2024 21:00:09 +0100 Subject: [PATCH 11/51] fixes and gpu based blurring --- src/livekit/useLiveKit.ts | 16 +++++++++------- src/room/LobbyView.tsx | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 6e563f1c..d33077d2 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -87,7 +87,7 @@ export function useLiveKit( let b = undefined; try { // eslint-disable-next-line new-cap - b = BackgroundBlur(15); + b = BackgroundBlur(15, { delegate: "GPU" }); } catch (e) { logger.error("disable background blur", e); } @@ -178,15 +178,17 @@ export function useLiveKit( if (publishCallback) room.off(RoomEvent.LocalTrackPublished, publishCallback); - if ( - videoTrack !== undefined && - videoTrack.track?.getProcessor() === undefined - ) { - if (showBackgroundBlur) { + if (videoTrack !== undefined) { + if ( + showBackgroundBlur && + videoTrack.track?.getProcessor()?.name !== "background-blur" + ) { logger.info("Blur: set blur"); void videoTrack.track?.setProcessor(blur); - } else { + } else if ( + videoTrack.track?.getProcessor()?.name === "background-blur" + ) { void videoTrack.track?.stopProcessor(); } } diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 6b5db196..486f872f 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -116,7 +116,7 @@ export const LobbyView: FC = ({ let b = undefined; try { // eslint-disable-next-line new-cap - b = BackgroundBlur(15); + b = BackgroundBlur(15, { delegate: "GPU" }); } catch (e) { logger.error("disable background blur", e); } From 9eae9190503e5171eb2c34db2aefacd7a4b5e284 Mon Sep 17 00:00:00 2001 From: Timo Date: Fri, 29 Nov 2024 18:18:40 +0100 Subject: [PATCH 12/51] make default false --- src/settings/settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 6f52a525..27d28011 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -88,7 +88,7 @@ export const videoInput = new Setting( undefined, ); -export const backgroundBlur = new Setting("background-blur", true); +export const backgroundBlur = new Setting("background-blur", false); export const showHandRaisedTimer = new Setting( "hand-raised-show-timer", From d179db1e8f9865617bedafef11cab44772162d32 Mon Sep 17 00:00:00 2001 From: Timo Date: Mon, 2 Dec 2024 11:55:22 +0100 Subject: [PATCH 13/51] more detailed error message --- src/room/LobbyView.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 486f872f..4f9d3a85 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -118,7 +118,10 @@ export const LobbyView: FC = ({ // eslint-disable-next-line new-cap b = BackgroundBlur(15, { delegate: "GPU" }); } catch (e) { - logger.error("disable background blur", e); + logger.error( + "disable background blur because its not supported by the platform.", + e, + ); } return b; }, []); From 95c3ca88592c61e2d70a383d607cec4de0d9e47d Mon Sep 17 00:00:00 2001 From: Timo Date: Mon, 2 Dec 2024 17:35:43 +0100 Subject: [PATCH 14/51] review --- src/livekit/useLiveKit.ts | 6 ++---- src/room/LobbyView.tsx | 10 ++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index d33077d2..85f3178b 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -19,7 +19,7 @@ import { useEffect, useMemo, useRef } from "react"; import E2EEWorker from "livekit-client/e2ee-worker?worker"; import { logger } from "matrix-js-sdk/src/logger"; import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession"; -import { BackgroundBlur } from "@livekit/track-processors"; +import { BackgroundBlur as backgroundBlur } from "@livekit/track-processors"; import { defaultLiveKitOptions } from "./options"; import { SFUConfig } from "./openIDSFU"; @@ -86,8 +86,7 @@ export function useLiveKit( const blur = useMemo(() => { let b = undefined; try { - // eslint-disable-next-line new-cap - b = BackgroundBlur(15, { delegate: "GPU" }); + b = backgroundBlur(15, { delegate: "GPU" }); } catch (e) { logger.error("disable background blur", e); } @@ -99,7 +98,6 @@ export function useLiveKit( videoCaptureDefaults: { ...defaultLiveKitOptions.videoCaptureDefaults, deviceId: initialDevices.current.videoInput.selectedId, - // eslint-disable-next-line new-cap processor: blur, }, audioCaptureDefaults: { diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 4f9d3a85..880fdc75 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -16,7 +16,7 @@ import { usePreviewTracks } from "@livekit/components-react"; import { LocalVideoTrack, Track } from "livekit-client"; import { useObservable } from "observable-hooks"; import { map } from "rxjs"; -import { BackgroundBlur } from "@livekit/track-processors"; +import { BackgroundBlur as backgroundBlur } from "@livekit/track-processors"; import inCallStyles from "./InCallView.module.css"; import styles from "./LobbyView.module.css"; @@ -115,8 +115,7 @@ export const LobbyView: FC = ({ const blur = useMemo(() => { let b = undefined; try { - // eslint-disable-next-line new-cap - b = BackgroundBlur(15, { delegate: "GPU" }); + b = backgroundBlur(15, { delegate: "GPU" }); } catch (e) { logger.error( "disable background blur because its not supported by the platform.", @@ -138,14 +137,14 @@ export const LobbyView: FC = ({ video: muteStates.video.enabled && { deviceId: devices.videoInput.selectedId, // It should be possible to set a processor here: - // processor: blur, - // This causes a crash currently hence we do the effect below... + processor: blur, }, }), [ initialAudioOptions, muteStates.video.enabled, devices.videoInput.selectedId, + blur, ], ); @@ -174,7 +173,6 @@ export const LobbyView: FC = ({ if (!blur) return; const updateBlur = async (showBlur: boolean): Promise => { if (showBlur && !videoTrack?.getProcessor()) { - // eslint-disable-next-line new-cap await videoTrack?.setProcessor(blur); } else { await videoTrack?.stopProcessor(); From c6d48446f40547fb2eeb77fc0de640cb7b783c36 Mon Sep 17 00:00:00 2001 From: Timo Date: Mon, 2 Dec 2024 17:45:32 +0100 Subject: [PATCH 15/51] review --- locales/en/app.json | 4 ++-- src/room/LobbyView.tsx | 1 - src/settings/SettingsModal.tsx | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/locales/en/app.json b/locales/en/app.json index 2b610560..6b72876a 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -151,8 +151,8 @@ "effect_volume_label": "Sound effect volume" }, "background_blur_header": "Background", - "background_blur_label": "Enable background blurring", - "blur_not_supported_by_browser": "(Background blur is not supported by this browser.)", + "background_blur_label": "Blur the background of the video", + "blur_not_supported_by_browser": "(Background blur is not supported by this browser)", "developer_settings_label": "Developer Settings", "developer_settings_label_description": "Expose developer settings in the settings window.", "developer_tab_title": "Developer", diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 880fdc75..118302c6 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -111,7 +111,6 @@ export const LobbyView: FC = ({ muteStates.audio.enabled && { deviceId: devices.audioInput.selectedId }, ); - // eslint-disable-next-line new-cap const blur = useMemo(() => { let b = undefined; try { diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 84583b8f..4e9da359 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -9,7 +9,7 @@ import { ChangeEvent, FC, ReactNode, useCallback, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { Root as Form, Separator, Text } from "@vector-im/compound-web"; -import { BackgroundBlur } from "@livekit/track-processors"; +import { BackgroundBlur as backgroundBlur } from "@livekit/track-processors"; import { logger } from "matrix-js-sdk/src/logger"; import { Modal } from "../Modal"; @@ -78,8 +78,7 @@ export const SettingsModal: FC = ({ const [blur, setBlur] = useSetting(backgroundBlurSetting); let canBlur = true; try { - // eslint-disable-next-line new-cap - BackgroundBlur(15); + backgroundBlur(15); } catch (e) { logger.debug( "Cannot blur, so we do not show the option in settings. error: ", From 574c89529a0d3392ddb4c56f18638118ede7300f Mon Sep 17 00:00:00 2001 From: Timo Date: Mon, 2 Dec 2024 17:51:31 +0100 Subject: [PATCH 16/51] disable blur in local track again --- src/room/LobbyView.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 118302c6..3626f82e 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -136,14 +136,13 @@ export const LobbyView: FC = ({ video: muteStates.video.enabled && { deviceId: devices.videoInput.selectedId, // It should be possible to set a processor here: - processor: blur, + // processor: blur, }, }), [ initialAudioOptions, muteStates.video.enabled, devices.videoInput.selectedId, - blur, ], ); From b77c4afff2a539a5e4b80c7a4ee83e9587eed102 Mon Sep 17 00:00:00 2001 From: Timo Date: Fri, 6 Dec 2024 18:12:51 +0100 Subject: [PATCH 17/51] refactor - still should not work without a fixing upstream LK: https://github.com/livekit/components-js/pull/1042 https://github.com/livekit/components-js/pull/1043 --- src/App.tsx | 54 ++++++++----- src/livekit/TrackProcessorContext.tsx | 111 ++++++++++++++++++++++++++ src/livekit/useLiveKit.ts | 90 +++++---------------- src/room/LobbyView.tsx | 60 +++++--------- src/settings/SettingsModal.tsx | 30 +++---- yarn.lock | 16 ++-- 6 files changed, 204 insertions(+), 157 deletions(-) create mode 100644 src/livekit/TrackProcessorContext.tsx diff --git a/src/App.tsx b/src/App.tsx index 8d841dba..5d96877b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -28,6 +28,7 @@ import { Initializer } from "./initializer"; import { MediaDevicesProvider } from "./livekit/MediaDevicesContext"; import { widget } from "./widget"; import { useTheme } from "./useTheme"; +import { ProcessorProvider } from "./livekit/TrackProcessorContext"; const SentryRoute = Sentry.withSentryRouting(Route); @@ -82,27 +83,25 @@ export const App: FC = ({ history }) => { {loaded ? ( - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + ) : ( @@ -113,3 +112,16 @@ export const App: FC = ({ history }) => { ); }; + +const Providers: FC<{ + children: JSX.Element; +}> = ({ children }) => { + // We use this to stack all used providers to not make the App component to verbose + return ( + + + {children} + + + ); +}; diff --git a/src/livekit/TrackProcessorContext.tsx b/src/livekit/TrackProcessorContext.tsx new file mode 100644 index 00000000..c2bc3826 --- /dev/null +++ b/src/livekit/TrackProcessorContext.tsx @@ -0,0 +1,111 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only +Please see LICENSE in the repository root for full details. +*/ + +import { + BackgroundBlur as backgroundBlur, + BackgroundOptions, + ProcessorWrapper, +} from "@livekit/track-processors"; +import { + createContext, + FC, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from "react"; +import { logger } from "matrix-js-sdk/src/logger"; +import { LocalVideoTrack } from "livekit-client"; + +import { + backgroundBlur as backgroundBlurSettings, + useSetting, +} from "../settings/settings"; + +type ProcessorState = { + supported: boolean | undefined; + processor: undefined | ProcessorWrapper; + /** + * Call this method to try to initialize a processor. + * This only needs to happen if supported is undefined. + * If the backgroundBlur setting is set to true this does not need to be called + * and the processorState.supported will update automatically to the correct value. + */ + checkSupported: () => void; +}; +const ProcessorContext = createContext(undefined); + +export const useTrackProcessor = (): ProcessorState | undefined => + useContext(ProcessorContext); + +export const useTrackProcessorSync = ( + videoTrack: LocalVideoTrack | null, +): void => { + const { processor } = useTrackProcessor() || {}; + useEffect(() => { + if (processor && !videoTrack?.getProcessor()) { + void videoTrack?.setProcessor(processor); + } + if (!processor && videoTrack?.getProcessor()) { + void videoTrack?.stopProcessor(); + } + }, [processor, videoTrack]); +}; + +interface Props { + children: JSX.Element; +} +export const ProcessorProvider: FC = ({ children }) => { + // The setting the user wants to have + const [blurActivated] = useSetting(backgroundBlurSettings); + + // If `ProcessorState.supported` is undefined the user can activate that we want + // to have it at least checked (this is useful to show the settings menu properly) + // We dont want to try initializing the blur if the user is not even looking at the setting + const [shouldCheckSupport, setShouldCheckSupport] = useState(blurActivated); + + // Cache the processor so we only need to initialize it once. + const blur = useRef | undefined>( + undefined, + ); + + const checkSupported = useCallback(() => { + setShouldCheckSupport(true); + }, []); + // This is the actual state exposed through the context + const [processorState, setProcessorState] = useState(() => ({ + supported: false, + processor: undefined, + checkSupported, + })); + + useEffect(() => { + if (!shouldCheckSupport) return; + try { + if (!blur.current) blur.current = backgroundBlur(15, { delegate: "GPU" }); + setProcessorState({ + checkSupported, + supported: true, + processor: blurActivated ? blur.current : undefined, + }); + } catch (e) { + setProcessorState({ + checkSupported, + supported: false, + processor: undefined, + }); + logger.error("disable background blur", e); + } + }, [blurActivated, checkSupported, shouldCheckSupport]); + + return ( + + {children} + + ); +}; diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 85f3178b..4836141e 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -9,9 +9,8 @@ import { ConnectionState, E2EEOptions, ExternalE2EEKeyProvider, - LocalTrackPublication, + LocalVideoTrack, Room, - RoomEvent, RoomOptions, Track, } from "livekit-client"; @@ -19,7 +18,6 @@ import { useEffect, useMemo, useRef } from "react"; import E2EEWorker from "livekit-client/e2ee-worker?worker"; import { logger } from "matrix-js-sdk/src/logger"; import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession"; -import { BackgroundBlur as backgroundBlur } from "@livekit/track-processors"; import { defaultLiveKitOptions } from "./options"; import { SFUConfig } from "./openIDSFU"; @@ -29,7 +27,6 @@ import { MediaDevices, useMediaDevices, } from "./MediaDevicesContext"; -import { backgroundBlur as backgroundBlurSettings } from "../settings/settings"; import { ECConnectionState, useECConnectionState, @@ -37,7 +34,11 @@ import { import { MatrixKeyProvider } from "../e2ee/matrixKeyProvider"; import { E2eeType } from "../e2ee/e2eeType"; import { EncryptionSystem } from "../e2ee/sharedKeyManagement"; -import { useSetting } from "../settings/settings"; +import { + useTrackProcessor, + useTrackProcessorSync, +} from "./TrackProcessorContext"; +import { useInitial } from "../useInitial"; interface UseLivekitResult { livekitRoom?: Room; @@ -83,22 +84,16 @@ export function useLiveKit( const initialMuteStates = useRef(muteStates); const devices = useMediaDevices(); const initialDevices = useRef(devices); - const blur = useMemo(() => { - let b = undefined; - try { - b = backgroundBlur(15, { delegate: "GPU" }); - } catch (e) { - logger.error("disable background blur", e); - } - return b; - }, []); + + const { processor } = useTrackProcessor() || {}; + const initialProcessor = useInitial(() => processor); const roomOptions = useMemo( (): RoomOptions => ({ ...defaultLiveKitOptions, videoCaptureDefaults: { ...defaultLiveKitOptions.videoCaptureDefaults, deviceId: initialDevices.current.videoInput.selectedId, - processor: blur, + processor: initialProcessor, }, audioCaptureDefaults: { ...defaultLiveKitOptions.audioCaptureDefaults, @@ -109,7 +104,7 @@ export function useLiveKit( }, e2ee: e2eeOptions, }), - [blur, e2eeOptions], + [e2eeOptions, initialProcessor], ); // Store if audio/video are currently updating. If to prohibit unnecessary calls @@ -134,6 +129,15 @@ export function useLiveKit( return r; }, [roomOptions, e2eeSystem]); + const videoTrack = useMemo( + () => + Array.from(room.localParticipant.videoTrackPublications.values()).find( + (v) => v.source === Track.Source.Camera, + )?.track as LocalVideoTrack | null, + [room.localParticipant.videoTrackPublications], + ); + useTrackProcessorSync(videoTrack); + const connectionState = useECConnectionState( { deviceId: initialDevices.current.audioInput.selectedId, @@ -143,58 +147,6 @@ export function useLiveKit( sfuConfig, ); - const [showBackgroundBlur] = useSetting(backgroundBlurSettings); - const videoTrackPromise = useRef< - undefined | Promise - >(undefined); - - useEffect(() => { - // Don't even try if we cannot blur on this platform - if (!blur) return; - if (!room || videoTrackPromise.current) return; - const update = async (): Promise => { - let publishCallback: undefined | ((track: LocalTrackPublication) => void); - videoTrackPromise.current = new Promise< - LocalTrackPublication | undefined - >((resolve) => { - const videoTrack = Array.from( - room.localParticipant.videoTrackPublications.values(), - ).find((v) => v.source === Track.Source.Camera); - if (videoTrack) { - resolve(videoTrack); - } - publishCallback = (videoTrack: LocalTrackPublication): void => { - if (videoTrack.source === Track.Source.Camera) { - resolve(videoTrack); - } - }; - room.on(RoomEvent.LocalTrackPublished, publishCallback); - }); - - const videoTrack = await videoTrackPromise.current; - - if (publishCallback) - room.off(RoomEvent.LocalTrackPublished, publishCallback); - - if (videoTrack !== undefined) { - if ( - showBackgroundBlur && - videoTrack.track?.getProcessor()?.name !== "background-blur" - ) { - logger.info("Blur: set blur"); - - void videoTrack.track?.setProcessor(blur); - } else if ( - videoTrack.track?.getProcessor()?.name === "background-blur" - ) { - void videoTrack.track?.stopProcessor(); - } - } - videoTrackPromise.current = undefined; - }; - void update(); - }, [blur, room, showBackgroundBlur]); - useEffect(() => { // Sync the requested mute states with LiveKit's mute states. We do it this // way around rather than using LiveKit as the source of truth, so that the @@ -261,6 +213,7 @@ export function useLiveKit( audioMuteUpdating.current = true; trackPublication = await participant.setMicrophoneEnabled( buttonEnabled.current.audio, + room.options.audioCaptureDefaults, ); audioMuteUpdating.current = false; break; @@ -268,6 +221,7 @@ export function useLiveKit( videoMuteUpdating.current = true; trackPublication = await participant.setCameraEnabled( buttonEnabled.current.video, + room.options.videoCaptureDefaults, ); videoMuteUpdating.current = false; break; diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 3626f82e..545eff09 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -13,10 +13,13 @@ import classNames from "classnames"; import { useHistory } from "react-router-dom"; import { logger } from "matrix-js-sdk/src/logger"; import { usePreviewTracks } from "@livekit/components-react"; -import { LocalVideoTrack, Track } from "livekit-client"; +import { + CreateLocalTracksOptions, + LocalVideoTrack, + Track, +} from "livekit-client"; import { useObservable } from "observable-hooks"; import { map } from "rxjs"; -import { BackgroundBlur as backgroundBlur } from "@livekit/track-processors"; import inCallStyles from "./InCallView.module.css"; import styles from "./LobbyView.module.css"; @@ -33,14 +36,16 @@ import { VideoButton, } from "../button/Button"; import { SettingsModal, defaultSettingsTab } from "../settings/SettingsModal"; -import { backgroundBlur as backgroundBlurSettings } from "../settings/settings"; import { useMediaQuery } from "../useMediaQuery"; import { E2eeType } from "../e2ee/e2eeType"; import { Link } from "../button/Link"; import { useMediaDevices } from "../livekit/MediaDevicesContext"; import { useInitial } from "../useInitial"; import { useSwitchCamera as useShowSwitchCamera } from "./useSwitchCamera"; -import { useSetting } from "../settings/settings"; +import { + useTrackProcessor, + useTrackProcessorSync, +} from "../livekit/TrackProcessorContext"; interface Props { client: MatrixClient; @@ -111,20 +116,10 @@ export const LobbyView: FC = ({ muteStates.audio.enabled && { deviceId: devices.audioInput.selectedId }, ); - const blur = useMemo(() => { - let b = undefined; - try { - b = backgroundBlur(15, { delegate: "GPU" }); - } catch (e) { - logger.error( - "disable background blur because its not supported by the platform.", - e, - ); - } - return b; - }, []); + const { processor } = useTrackProcessor() || {}; - const localTrackOptions = useMemo( + const initialProcessor = useInitial(() => processor); + const localTrackOptions = useMemo( () => ({ // The only reason we request audio here is to get the audio permission // request over with at the same time. But changing the audio settings @@ -135,14 +130,14 @@ export const LobbyView: FC = ({ audio: Object.assign({}, initialAudioOptions), video: muteStates.video.enabled && { deviceId: devices.videoInput.selectedId, - // It should be possible to set a processor here: - // processor: blur, + processor: initialProcessor, }, }), [ initialAudioOptions, muteStates.video.enabled, devices.videoInput.selectedId, + initialProcessor, ], ); @@ -157,28 +152,11 @@ export const LobbyView: FC = ({ const tracks = usePreviewTracks(localTrackOptions, onError); - const videoTrack = useMemo( - () => - (tracks?.find((t) => t.kind === Track.Kind.Video) ?? - null) as LocalVideoTrack | null, - [tracks], - ); - - const [showBackgroundBlur] = useSetting(backgroundBlurSettings); - - useEffect(() => { - // Fon't even try if we cannot blur on this platform - if (!blur) return; - const updateBlur = async (showBlur: boolean): Promise => { - if (showBlur && !videoTrack?.getProcessor()) { - await videoTrack?.setProcessor(blur); - } else { - await videoTrack?.stopProcessor(); - } - }; - if (videoTrack) void updateBlur(showBackgroundBlur); - }, [videoTrack, showBackgroundBlur, blur]); - + const videoTrack = useMemo(() => { + const track = tracks?.find((t) => t.kind === Track.Kind.Video); + return track as LocalVideoTrack | null; + }, [tracks]); + useTrackProcessorSync(videoTrack); const showSwitchCamera = useShowSwitchCamera( useObservable( (inputs) => inputs.pipe(map(([video]) => video)), diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 4e9da359..11d73f4d 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -5,12 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { ChangeEvent, FC, ReactNode, useCallback, useState } from "react"; +import { ChangeEvent, FC, ReactNode, useCallback, useEffect, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { Root as Form, Separator, Text } from "@vector-im/compound-web"; -import { BackgroundBlur as backgroundBlur } from "@livekit/track-processors"; -import { logger } from "matrix-js-sdk/src/logger"; import { Modal } from "../Modal"; import styles from "./SettingsModal.module.css"; @@ -36,6 +34,7 @@ import { isFirefox } from "../Platform"; import { PreferencesSettingsTab } from "./PreferencesSettingsTab"; import { Slider } from "../Slider"; import { DeviceSelection } from "./DeviceSelection"; +import { useTrackProcessor } from "../livekit/TrackProcessorContext"; type SettingsTab = | "audio" @@ -75,18 +74,11 @@ export const SettingsModal: FC = ({ // Generate a `Checkbox` input to turn blur on or off. const BlurCheckbox: React.FC = (): ReactNode => { - const [blur, setBlur] = useSetting(backgroundBlurSetting); - let canBlur = true; - try { - backgroundBlur(15); - } catch (e) { - logger.debug( - "Cannot blur, so we do not show the option in settings. error: ", - e, - ); - canBlur = false; - setBlur(false); - } + const { supported, checkSupported } = useTrackProcessor() || {}; + useEffect(() => checkSupported?.(), [checkSupported]); + + const [blurActive, setBlurActive] = useSetting(backgroundBlurSetting); + return ( <>

{t("settings.background_blur_header")}

@@ -96,12 +88,12 @@ export const SettingsModal: FC = ({ id="activateBackgroundBlur" label={t("settings.background_blur_label")} description={ - canBlur ? "" : t("settings.blur_not_supported_by_browser") + supported ? "" : t("settings.blur_not_supported_by_browser") } type="checkbox" - checked={blur} - onChange={(b): void => setBlur(b.target.checked)} - disabled={!canBlur} + checked={!!blurActive} + onChange={(b): void => setBlurActive(b.target.checked)} + disabled={!supported} /> diff --git a/yarn.lock b/yarn.lock index 1a456433..b4fc53aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1805,10 +1805,10 @@ resolved "https://registry.yarnpkg.com/@livekit/mutex/-/mutex-1.0.0.tgz#9493102d92ff75dfb0445eccc46c7c7ac189d385" integrity sha512-aiUhoThBNF9UyGTxEURFzJLhhPLIVTnQiEVMjRhPnfHNKLfo2JY9xovHKIus7B78UD5hsP6DlgpmAsjrz4U0Iw== -"@livekit/protocol@1.24.0": - version "1.24.0" - resolved "https://registry.yarnpkg.com/@livekit/protocol/-/protocol-1.24.0.tgz#b23acab25c11027bf26c1b42f9b782682f2da585" - integrity sha512-9dCsqnkMn7lvbI4NGh18zhLDsrXyUcpS++TEFgEk5Xv1WM3R2kT3EzqgL1P/mr3jaabM6rJ8wZA/KJLuQNpF5w== +"@livekit/protocol@1.29.3": + version "1.29.3" + resolved "https://registry.yarnpkg.com/@livekit/protocol/-/protocol-1.29.3.tgz#486ce215c0c591ad64036d9b13c7e28f5417cf03" + integrity sha512-5La/pm2LsSeCbm7xNe/TvHGYu7uVwDpLrlycpgo5nzofGq/TH67255vS8ni/1Y7vrFuAI8VYG/s42mcC1UF6tQ== dependencies: "@bufbuild/protobuf" "^1.10.0" @@ -6125,12 +6125,12 @@ lines-and-columns@^1.1.6: integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== livekit-client@^2.5.7: - version "2.7.0" - resolved "https://registry.yarnpkg.com/livekit-client/-/livekit-client-2.7.0.tgz#d7a80aff4ad335dd093b0c90d0d715466539651a" - integrity sha512-4vjfSReFNAUD+2oLUz9qFRWztJaI/+AexpOmCgizNsPYpvvqgAvEGxapnhuAug9uP7JVYaKPXaTCq90MWZoDHg== + version "2.7.3" + resolved "https://registry.yarnpkg.com/livekit-client/-/livekit-client-2.7.3.tgz#70a5f5016f3f50b1282f4b9090aa17a39f8bde09" + integrity sha512-oHEmUTFjIJARi5R87PsobZx8y2HCSUwla3Nu71EqDOAMnNY9aoGMLsJVao5Y+v1TSk71rgRm991fihgxtbg5xw== dependencies: "@livekit/mutex" "1.0.0" - "@livekit/protocol" "1.24.0" + "@livekit/protocol" "1.29.3" events "^3.3.0" loglevel "^1.8.0" sdp-transform "^2.14.1" From 59bc73cedfbd14ba22cf97a0e5db4cf4116af033 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 9 Dec 2024 22:04:09 +0000 Subject: [PATCH 18/51] Use local assets for blurring --- package.json | 1 + src/livekit/BlurBackgroundTransformer.ts | 62 ++++++++++++++++++ src/livekit/TrackProcessorContext.tsx | 14 ++-- src/mediapipe/imageSegmenter/README.md | 5 ++ .../imageSegmenter/selfie_segmenter.tflite | Bin 0 -> 249537 bytes yarn.lock | 5 ++ 6 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 src/livekit/BlurBackgroundTransformer.ts create mode 100644 src/mediapipe/imageSegmenter/README.md create mode 100644 src/mediapipe/imageSegmenter/selfie_segmenter.tflite diff --git a/package.json b/package.json index 36e03468..30f967e8 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@livekit/components-core": "^0.11.0", "@livekit/components-react": "^2.0.0", "@livekit/track-processors": "^0.3.2", + "@mediapipe/tasks-vision": "^0.10.18", "@opentelemetry/api": "^1.4.0", "@opentelemetry/core": "^1.25.1", "@opentelemetry/exporter-trace-otlp-http": "^0.55.0", diff --git a/src/livekit/BlurBackgroundTransformer.ts b/src/livekit/BlurBackgroundTransformer.ts new file mode 100644 index 00000000..7273e487 --- /dev/null +++ b/src/livekit/BlurBackgroundTransformer.ts @@ -0,0 +1,62 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only +Please see LICENSE in the repository root for full details. +*/ + +import { + BackgroundTransformer, + VideoTransformer, + VideoTransformerInitOptions, +} from "@livekit/track-processors"; +import { ImageSegmenter } from "@mediapipe/tasks-vision"; + +interface WasmFileset { + /** The path to the Wasm loader script. */ + wasmLoaderPath: string; + /** The path to the Wasm binary. */ + wasmBinaryPath: string; +} + +// n.b. this only includes the SIMD versions of the WASM files which have good support: +// https://caniuse.com/?search=simd +const wasmFileset: WasmFileset = { + wasmLoaderPath: new URL( + "../../node_modules/@mediapipe/tasks-vision/wasm/vision_wasm_internal.js", + import.meta.url, + ).href, + wasmBinaryPath: new URL( + "../../node_modules/@mediapipe/tasks-vision/wasm/vision_wasm_internal.wasm", + import.meta.url, + ).href, +}; + +const modelAssetPath = new URL( + "../mediapipe/imageSegmenter/selfie_segmenter.tflite", + import.meta.url, +).href; + +export class BlurBackgroundTransformer extends BackgroundTransformer { + public async init({ + outputCanvas, + inputElement: inputVideo, + }: VideoTransformerInitOptions): Promise { + // call super.super.init() + await VideoTransformer.prototype.init.call(this, { + outputCanvas, + inputElement: inputVideo, + }); + + this.imageSegmenter = await ImageSegmenter.createFromOptions(wasmFileset, { + baseOptions: { + modelAssetPath, + delegate: "GPU", + ...this.options.segmenterOptions, + }, + runningMode: "VIDEO", + outputCategoryMask: true, + outputConfidenceMasks: false, + }); + } +} diff --git a/src/livekit/TrackProcessorContext.tsx b/src/livekit/TrackProcessorContext.tsx index c2bc3826..71714b6e 100644 --- a/src/livekit/TrackProcessorContext.tsx +++ b/src/livekit/TrackProcessorContext.tsx @@ -5,11 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { - BackgroundBlur as backgroundBlur, - BackgroundOptions, - ProcessorWrapper, -} from "@livekit/track-processors"; +import { BackgroundOptions, ProcessorWrapper } from "@livekit/track-processors"; import { createContext, FC, @@ -26,6 +22,7 @@ import { backgroundBlur as backgroundBlurSettings, useSetting, } from "../settings/settings"; +import { BlurBackgroundTransformer } from "./BlurBackgroundTransformer"; type ProcessorState = { supported: boolean | undefined; @@ -87,7 +84,12 @@ export const ProcessorProvider: FC = ({ children }) => { useEffect(() => { if (!shouldCheckSupport) return; try { - if (!blur.current) blur.current = backgroundBlur(15, { delegate: "GPU" }); + if (!blur.current) { + blur.current = new ProcessorWrapper( + new BlurBackgroundTransformer({ blurRadius: 15 }), + "background-blur", + ); + } setProcessorState({ checkSupported, supported: true, diff --git a/src/mediapipe/imageSegmenter/README.md b/src/mediapipe/imageSegmenter/README.md new file mode 100644 index 00000000..39bea2d8 --- /dev/null +++ b/src/mediapipe/imageSegmenter/README.md @@ -0,0 +1,5 @@ +# Google AI Edge MediaPipe Selfie Segmentation + +- See: https://ai.google.dev/edge/mediapipe/solutions/vision/image_segmenter +- Latest: https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite +- License: Apache 2.0 as per https://storage.googleapis.com/mediapipe-assets/Model%20Card%20MediaPipe%20Selfie%20Segmentation.pdf diff --git a/src/mediapipe/imageSegmenter/selfie_segmenter.tflite b/src/mediapipe/imageSegmenter/selfie_segmenter.tflite new file mode 100644 index 0000000000000000000000000000000000000000..a4ebd4777ede40ba04abc8e5c28a73407cb7a1a0 GIT binary patch literal 249537 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

SDFw!?va%i&|4Ps44A)N#z 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%0DlFro3}!1yvBBc;|pdU=F{vN z+SDGFcTMcu1`b*$;N^Bm&Tp>W3mo70>*$yJ(MRhH;>E2S&4O!u+2<%6N7tL{r*?XI zs=)E71h3Bm81Op2wQpJUd>4+RW$SIPO9~<`N4wt1KJZE2#q<06Rzx*lL6Atm`wh{L zea%nKZSzc5;>R(!^@Z9tj$^59#Ge*jyXd23w%M9E0B}k0_Yv*bme$09Adv#^7mR*A z`3VZ9`VqhO`DhRKL~=?q>^@xg+vo?*ZWA~)mY0sC|&Kn6{_pq(mEq>ujw2yv6%huc0>;(WP z1kd@=viy|R>;*w00q@7ar#vT9a~Q|ecWN8)JAM~!tBP*NHe2%& ze^~HU@#5H)*1Y7XZ6*4P;OkoTJLR6pWR3ZDBk$xlo-HOJ0z(ad4;Q}uvH=dk#l zzmGns5?<~D`FpEqM~OL0!B-08KB)gLLG>d?=L_f`0-rwMI`DaNte^5{Gy%s>3N~5K z>H$G&3-NdSA=*-16gjq}_3#eCSH+uS%dP6+67y{XpSHB;rZIPo4hv81(miWyLXA_- zbK1<~<|YKB`or>uu10?hV+^!Tz>Axkq{!epm5^EC-Ge?_XAm!LZZgG&&ab(t1c&2j zyxZD|`$zFs-=hg$a?^;H%Q*hPAESMDqmP#6Cj7lr{KkRWwN>zM5qxqks{g)meC z`UQeSO1!TFU)QQ9H(@h*YtN}j^b^o^EPl=w?4=r17 zTlbd$TonB4i3UDh@3ihO2@(l-UvANln^Emodx)R>YxLvm&_~Pkvvqs`;E>>7OSGe3 zTE_>1L<+nwfUo}U$uBthruq?oLhw$0aqqQ|bHU7E^NX!*txh}s7JU%FMaz7U*0zTQ zkDtG&@aY=es!3I=+Xnd2m@$bFD`W<%=ynqLZVIF81<%`Y|<18|fAa>U@tll-y;-P*o@ zKBa*4|0CMR?*yV%>&<5z%9GNkTC)P5PY9m2pjCK2m#*=0+laOl1c-3mhrsPwYkb!- z{i3D%AOKGxZ&r9*Yh%*{{+roE{3X$meYDgjE#E8Mh#B~2^a0luEw!mG->Dn%YSR?a z;8G{XOmnVp?&$}7P9b(ku#6=)1`)ie{p6h%{a%eeS|{MejX_Da)Q`N&!efnz)*gc< z>FWGzK%nS2xsA>J+yESf*mXCWcr|Oa8HZfko&=u0-#KL+UCE6ufS7sn{XPGL@BlReK8zqYN5K85&Y(UIR7MoZU9OZ>{`#P|It+QeLk zmhSnw#HSs^>snFw>%MN|IF#u|{JhM?doO6IZd$Tea}a-A@c3ybwp6#eBBgG`TiuR; zYyGC*P3AzlCSCL?#63u$qn9)f6d@IkK}!pul4vAzx!tNGsi|tZP)U(w*8pk zd9G8uIlt6r%hY!2@)Wpyr(I1h&aY31IV;#CzL0@)JBT0tZ?uDbwA2nQb1OTDDFjbF z(9$)mGcN5Q-ui48xcrR!o4F+W9vnW?wY0pagx70-Q$KaVqwx4yGY9{lJ_o}`^&DOF zDa1|uFZuyL5ysXDc;Wda<=eV}yanO0wn6Izym+}sA3Ldinm;(kJjS43*ylLaJwLoF zwnU%8tS`$Z7r*X>XcxyqOV?LR_&QFzf~S6pH*s2LLXJm$>^hBut9CFxq;_@n zDh!rBkNeHH;ezON8*pgVZ^$!;L{9TulllxGv+8fbGB2r)RiT_1GMVNBU6lTf!nTO`)58S Date: Mon, 9 Dec 2024 22:09:26 +0000 Subject: [PATCH 19/51] Lint --- src/settings/SettingsModal.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 11d73f4d..b7d4c846 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -5,7 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { ChangeEvent, FC, ReactNode, useCallback, useEffect, useState } from "react"; +import { + ChangeEvent, + FC, + ReactNode, + useCallback, + useEffect, + useState, +} from "react"; import { Trans, useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { Root as Form, Separator, Text } from "@vector-im/compound-web"; From aa5cf2f5918b8328e3a3431f9b259e3936d583a7 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 9 Dec 2024 22:11:30 +0000 Subject: [PATCH 20/51] Wording change --- locales/en/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en/app.json b/locales/en/app.json index 6b72876a..7b57e125 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -152,7 +152,7 @@ }, "background_blur_header": "Background", "background_blur_label": "Blur the background of the video", - "blur_not_supported_by_browser": "(Background blur is not supported by this browser)", + "blur_not_supported_by_browser": "(Background blur is not supported by this device)", "developer_settings_label": "Developer Settings", "developer_settings_label_description": "Expose developer settings in the settings window.", "developer_tab_title": "Developer", From b6cc9c534ba749e327790a11f0cbc91599162141 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 9 Dec 2024 22:12:49 +0000 Subject: [PATCH 21/51] Lint --- src/room/LobbyView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 545eff09..0321672b 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { FC, useCallback, useEffect, useMemo, useState } from "react"; +import { FC, useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { Button } from "@vector-im/compound-web"; From c860be73f6a2cc0bbdd6455b2704710b8d18ecfc Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Wed, 11 Dec 2024 10:30:45 +0000 Subject: [PATCH 22/51] . --- src/settings/SettingsModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 58703ad4..46839b0d 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -27,7 +27,7 @@ import { import { widget } from "../widget"; import { useSetting, - developerSettingsTab as developerSettingsTabSetting, + developerSettingsTab, backgroundBlur as backgroundBlurSetting, soundEffectVolumeSetting, } from "./settings"; @@ -102,7 +102,7 @@ export const SettingsModal: FC = ({ const [soundVolume, setSoundVolume] = useSetting(soundEffectVolumeSetting); const [soundVolumeRaw, setSoundVolumeRaw] = useState(soundVolume); - const [showDeveloperSettingsTab] = useSetting(developerSettingsTabSetting); + const [showDeveloperSettingsTab] = useSetting(developerSettingsTab); const audioTab: Tab = { key: "audio", From f18ce5adf1563e7af4c0e153665c54fc61f955d3 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Wed, 11 Dec 2024 10:34:24 +0000 Subject: [PATCH 23/51] Format --- src/settings/SettingsModal.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 46839b0d..e3bd5017 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -5,12 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { - type FC, - type ReactNode, - useEffect, - useState, -} from "react"; +import { type FC, type ReactNode, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { type MatrixClient } from "matrix-js-sdk/src/matrix"; import { Root as Form, Separator } from "@vector-im/compound-web"; From 3f48810034a4a13ba737635fff92e3ba5d2d7f99 Mon Sep 17 00:00:00 2001 From: Timo Date: Fri, 13 Dec 2024 09:57:55 +0100 Subject: [PATCH 24/51] bump components-js (not yet working) --- src/livekit/TrackProcessorContext.tsx | 18 +++++++++++------- yarn.lock | 6 +++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/livekit/TrackProcessorContext.tsx b/src/livekit/TrackProcessorContext.tsx index 71714b6e..e131dcb1 100644 --- a/src/livekit/TrackProcessorContext.tsx +++ b/src/livekit/TrackProcessorContext.tsx @@ -5,10 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { BackgroundOptions, ProcessorWrapper } from "@livekit/track-processors"; +import { + type BackgroundOptions, + ProcessorWrapper, +} from "@livekit/track-processors"; import { createContext, - FC, + type FC, useCallback, useContext, useEffect, @@ -16,7 +19,7 @@ import { useState, } from "react"; import { logger } from "matrix-js-sdk/src/logger"; -import { LocalVideoTrack } from "livekit-client"; +import { type LocalVideoTrack } from "livekit-client"; import { backgroundBlur as backgroundBlurSettings, @@ -45,11 +48,12 @@ export const useTrackProcessorSync = ( ): void => { const { processor } = useTrackProcessor() || {}; useEffect(() => { - if (processor && !videoTrack?.getProcessor()) { - void videoTrack?.setProcessor(processor); + if (!videoTrack) return; + if (processor && !videoTrack.getProcessor()) { + void videoTrack.setProcessor(processor); } - if (!processor && videoTrack?.getProcessor()) { - void videoTrack?.stopProcessor(); + if (!processor && videoTrack.getProcessor()) { + void videoTrack.stopProcessor(); } }, [processor, videoTrack]); }; diff --git a/yarn.lock b/yarn.lock index 830228c1..880d8e52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1941,9 +1941,9 @@ rxjs "7.8.1" "@livekit/components-react@^2.0.0": - version "2.6.9" - resolved "https://registry.yarnpkg.com/@livekit/components-react/-/components-react-2.6.9.tgz#2ff4691dc2cae6ed4c4b2e586a255d00e494bf9c" - integrity sha512-j43i/Dm8dlI2jxv5wv0s+69QPVqVEjg0y2tyznfs/7RDcaIZsIIzNijPu1kLditerzvzQdRsOgFQ3UWONcTkGA== + version "2.6.10" + resolved "https://registry.yarnpkg.com/@livekit/components-react/-/components-react-2.6.10.tgz#949d6e65e8507e2d8a4c75bf190adf56f6358175" + integrity sha512-aR8rqCIEvT3QYHuVEm67THRmNd9x25FTmU3Phi928FhzQJXDBO0N1/5d6qEE/wuDOgXMOoBgA98qsdYDIi2f+g== dependencies: "@livekit/components-core" "0.11.10" clsx "2.1.1" From 7983adb98e3b40ce10016c7f025a6297ea08bf4f Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Wed, 18 Dec 2024 09:42:52 +0000 Subject: [PATCH 25/51] Lint --- src/livekit/BlurBackgroundTransformer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/livekit/BlurBackgroundTransformer.ts b/src/livekit/BlurBackgroundTransformer.ts index 7273e487..51b6a536 100644 --- a/src/livekit/BlurBackgroundTransformer.ts +++ b/src/livekit/BlurBackgroundTransformer.ts @@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details. import { BackgroundTransformer, VideoTransformer, - VideoTransformerInitOptions, + type VideoTransformerInitOptions, } from "@livekit/track-processors"; import { ImageSegmenter } from "@mediapipe/tasks-vision"; From 7c985b0d9c823b9e99e4fdd49c1da3fd85e6313f Mon Sep 17 00:00:00 2001 From: Timo Date: Thu, 19 Dec 2024 17:26:14 +0100 Subject: [PATCH 26/51] Revert "Bump matrix-js-sdk for bundle improvements (#2911)" This reverts commit 6b8dddfaaad68fd0bd1f4f5c0c2fb7b351775ffe. --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 64d418fc..0460f4bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1969,10 +1969,10 @@ "@mediapipe/holistic" "0.5.1675471629" "@mediapipe/tasks-vision" "0.10.9" -"@matrix-org/matrix-sdk-crypto-wasm@^12.0.0": - version "12.0.0" - resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-12.0.0.tgz#e3a5150ccbb21d5e98ee3882e7057b9f17fb962a" - integrity sha512-nkkXAxUIk9UTso4TbU6Bgqsv/rJShXQXRx0ti/W+AWXHJ2HoH4sL5LsXkc7a8yYGn8tyXqxGPsYA1UeHqLwm0Q== +"@matrix-org/matrix-sdk-crypto-wasm@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-11.0.0.tgz#c49a1a0d1e367d3c00a2144a4ab23caee0b1eec2" + integrity sha512-a7NUH8Kjc8hwzNCPpkOGXoceFqWJiWvA8OskXeDrKyODJuDz4yKrZ/nvgaVRfQe45Ab5UC1ZXYqaME+ChlJuqg== "@matrix-org/olm@3.2.15": version "3.2.15" @@ -6603,10 +6603,10 @@ matrix-events-sdk@0.0.1: matrix-js-sdk@matrix-org/matrix-js-sdk#develop: version "34.13.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/e4182eb75227c283a18704727021e99ced72868d" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/d1de32ea2773df4c6f8a956678bbd19b6d022475" dependencies: "@babel/runtime" "^7.12.5" - "@matrix-org/matrix-sdk-crypto-wasm" "^12.0.0" + "@matrix-org/matrix-sdk-crypto-wasm" "^11.0.0" "@matrix-org/olm" "3.2.15" another-json "^0.2.0" bs58 "^6.0.0" From e0f4bf8eab514a96177d4b970c8ece05d238a87b Mon Sep 17 00:00:00 2001 From: Timo Date: Thu, 19 Dec 2024 17:39:34 +0100 Subject: [PATCH 27/51] working version --- src/livekit/TrackProcessorContext.tsx | 17 +++++++++++------ src/livekit/useLiveKit.ts | 8 +++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/livekit/TrackProcessorContext.tsx b/src/livekit/TrackProcessorContext.tsx index e131dcb1..1fc0005a 100644 --- a/src/livekit/TrackProcessorContext.tsx +++ b/src/livekit/TrackProcessorContext.tsx @@ -6,8 +6,9 @@ Please see LICENSE in the repository root for full details. */ import { + BackgroundBlur as backgroundBlur, + type ProcessorWrapper, type BackgroundOptions, - ProcessorWrapper, } from "@livekit/track-processors"; import { createContext, @@ -25,7 +26,6 @@ import { backgroundBlur as backgroundBlurSettings, useSetting, } from "../settings/settings"; -import { BlurBackgroundTransformer } from "./BlurBackgroundTransformer"; type ProcessorState = { supported: boolean | undefined; @@ -89,10 +89,15 @@ export const ProcessorProvider: FC = ({ children }) => { if (!shouldCheckSupport) return; try { if (!blur.current) { - blur.current = new ProcessorWrapper( - new BlurBackgroundTransformer({ blurRadius: 15 }), - "background-blur", - ); + // TODO: move to our own local version of the transformer. + // Currently this is broken: error when trying to pipe IndexSizeError: Failed to construct 'ImageData': The source width is zero or not a number. + // blur.current = new ProcessorWrapper( + // new BlurBackgroundTransformer({}), + // "background-blur", + // ); + + // eslint-disable-next-line new-cap + blur.current = backgroundBlur(); } setProcessorState({ checkSupported, diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 72a972a7..aca6ed92 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -134,8 +134,14 @@ export function useLiveKit( Array.from(room.localParticipant.videoTrackPublications.values()).find( (v) => v.source === Track.Source.Camera, )?.track as LocalVideoTrack | null, - [room.localParticipant.videoTrackPublications], + [ + room.localParticipant.videoTrackPublications, + // We need to update on map changes + // eslint-disable-next-line react-hooks/exhaustive-deps + room.localParticipant.videoTrackPublications.keys(), + ], ); + useTrackProcessorSync(videoTrack); const connectionState = useECConnectionState( From bb0e1f38dc08ffea8c79c9a6678c5e8b09354bb3 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 6 Jan 2025 15:48:43 +0000 Subject: [PATCH 28/51] Exclude tasks-vision from knip --- knip.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/knip.ts b/knip.ts index 5ffe03c4..a0250354 100644 --- a/knip.ts +++ b/knip.ts @@ -29,6 +29,8 @@ export default { // element call side. "@types/dom-mediacapture-transform", "matrix-widget-api", + // we bundle files from this and lazy load them: + "@mediapipe/tasks-vision", ], ignoreExportsUsedInFile: true, } satisfies KnipConfig; From 96ea920e9892a0de713a51e7775c949e5ccb0a8c Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 6 Jan 2025 15:53:31 +0000 Subject: [PATCH 29/51] Try reenabling local implementation of BlurBackgroundTransformer --- src/livekit/TrackProcessorContext.tsx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/livekit/TrackProcessorContext.tsx b/src/livekit/TrackProcessorContext.tsx index 1fc0005a..8edec4f5 100644 --- a/src/livekit/TrackProcessorContext.tsx +++ b/src/livekit/TrackProcessorContext.tsx @@ -6,8 +6,7 @@ Please see LICENSE in the repository root for full details. */ import { - BackgroundBlur as backgroundBlur, - type ProcessorWrapper, + ProcessorWrapper, type BackgroundOptions, } from "@livekit/track-processors"; import { @@ -26,6 +25,7 @@ import { backgroundBlur as backgroundBlurSettings, useSetting, } from "../settings/settings"; +import { BlurBackgroundTransformer } from "./BlurBackgroundTransformer"; type ProcessorState = { supported: boolean | undefined; @@ -89,15 +89,10 @@ export const ProcessorProvider: FC = ({ children }) => { if (!shouldCheckSupport) return; try { if (!blur.current) { - // TODO: move to our own local version of the transformer. - // Currently this is broken: error when trying to pipe IndexSizeError: Failed to construct 'ImageData': The source width is zero or not a number. - // blur.current = new ProcessorWrapper( - // new BlurBackgroundTransformer({}), - // "background-blur", - // ); - - // eslint-disable-next-line new-cap - blur.current = backgroundBlur(); + blur.current = new ProcessorWrapper( + new BlurBackgroundTransformer({}), + "background-blur", + ); } setProcessorState({ checkSupported, From 4c4d01211c55a9ac6d33b0a1e7b28b2cfa358a1c Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Wed, 8 Jan 2025 14:20:12 +0000 Subject: [PATCH 30/51] Fix @matrix-org/matrix-sdk-crypto-wasm version --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index e7bcb72e..6bafd620 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1847,10 +1847,10 @@ "@mediapipe/holistic" "0.5.1675471629" "@mediapipe/tasks-vision" "0.10.9" -"@matrix-org/matrix-sdk-crypto-wasm@^11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-11.0.0.tgz#c49a1a0d1e367d3c00a2144a4ab23caee0b1eec2" - integrity sha512-a7NUH8Kjc8hwzNCPpkOGXoceFqWJiWvA8OskXeDrKyODJuDz4yKrZ/nvgaVRfQe45Ab5UC1ZXYqaME+ChlJuqg== +"@matrix-org/matrix-sdk-crypto-wasm@^12.0.0": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-12.0.0.tgz#e3a5150ccbb21d5e98ee3882e7057b9f17fb962a" + integrity sha512-nkkXAxUIk9UTso4TbU6Bgqsv/rJShXQXRx0ti/W+AWXHJ2HoH4sL5LsXkc7a8yYGn8tyXqxGPsYA1UeHqLwm0Q== "@matrix-org/olm@3.2.15": version "3.2.15" @@ -6443,10 +6443,10 @@ matrix-events-sdk@0.0.1: matrix-js-sdk@matrix-org/matrix-js-sdk#develop: version "34.13.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/d1de32ea2773df4c6f8a956678bbd19b6d022475" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/e4182eb75227c283a18704727021e99ced72868d" dependencies: "@babel/runtime" "^7.12.5" - "@matrix-org/matrix-sdk-crypto-wasm" "^11.0.0" + "@matrix-org/matrix-sdk-crypto-wasm" "^12.0.0" "@matrix-org/olm" "3.2.15" another-json "^0.2.0" bs58 "^6.0.0" From 00d724a3a96df21faf643d3b8a6d5d762eccbfa0 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Wed, 8 Jan 2025 14:27:19 +0000 Subject: [PATCH 31/51] Bump @livekit/track-processors to ignore empty frames --- package.json | 4 ++-- yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index bbe84e90..8a66a034 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@formatjs/intl-segmenter": "^11.7.3", "@livekit/components-core": "^0.11.0", "@livekit/components-react": "^2.0.0", - "@livekit/track-processors": "^0.3.2", + "@livekit/track-processors": "^0.3.3", "@mediapipe/tasks-vision": "^0.10.18", "@opentelemetry/api": "^1.4.0", "@opentelemetry/core": "^1.25.1", @@ -124,4 +124,4 @@ "resolutions": { "strip-ansi": "6.0.1" } -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 6bafd620..7ebda63d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1839,10 +1839,10 @@ dependencies: "@bufbuild/protobuf" "^1.10.0" -"@livekit/track-processors@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@livekit/track-processors/-/track-processors-0.3.2.tgz#eaff6a48b556c25e85f5dd2c4daf6dcf1bc3b143" - integrity sha512-4JUCzb7yIKoVsTo8J6FTzLZJHcI6DihfX/pGRDg0SOGaxprcDPrt8jaDBBTsnGBSXHeMxl2ugN+xQjdCWzLKEA== +"@livekit/track-processors@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@livekit/track-processors/-/track-processors-0.3.3.tgz#c44ec54229bf21ccb404b96c43702bc263c4f05d" + integrity sha512-C5mBWe34ie0pfhCf9zAD65D26ZytzW/xGrj8Zc1BGh5kLISFDVQlefO78LZWw9K5saTamk60O4fzSCKqWMPMLA== dependencies: "@mediapipe/holistic" "0.5.1675471629" "@mediapipe/tasks-vision" "0.10.9" From 6884b19a7336cf9aa7b8064fbb886bfee539b541 Mon Sep 17 00:00:00 2001 From: Timo Date: Sat, 5 Apr 2025 00:01:56 +0200 Subject: [PATCH 32/51] package and lock file --- package.json | 2 +- yarn.lock | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 03facac7..2041dbff 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,9 @@ "@formatjs/intl-segmenter": "^11.7.3", "@livekit/components-core": "^0.12.0", "@livekit/components-react": "^2.0.0", + "@livekit/protocol": "^1.33.0", "@livekit/track-processors": "^0.3.3", "@mediapipe/tasks-vision": "^0.10.18", - "@livekit/protocol": "^1.33.0", "@opentelemetry/api": "^1.4.0", "@opentelemetry/core": "^1.25.1", "@opentelemetry/exporter-trace-otlp-http": "^0.57.0", diff --git a/yarn.lock b/yarn.lock index c2507f4b..2216ae77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2545,6 +2545,18 @@ __metadata: languageName: node linkType: hard +"@livekit/track-processors@npm:^0.3.3": + version: 0.3.3 + resolution: "@livekit/track-processors@npm:0.3.3" + dependencies: + "@mediapipe/holistic": "npm:0.5.1675471629" + "@mediapipe/tasks-vision": "npm:0.10.9" + peerDependencies: + livekit-client: ^1.12.0 || ^2.1.0 + checksum: 10c0/a33478a340f89ae489ac2e1dcfbc1c69744943301c5f3bd22f604b63b188ef0feebe065d4cf084fcb53d9766d10c7655f8045e19549772471d52d3a976c36778 + languageName: node + linkType: hard + "@matrix-org/matrix-sdk-crypto-wasm@npm:^14.0.1": version: 14.0.1 resolution: "@matrix-org/matrix-sdk-crypto-wasm@npm:14.0.1" @@ -2559,6 +2571,27 @@ __metadata: languageName: node linkType: hard +"@mediapipe/holistic@npm:0.5.1675471629": + version: 0.5.1675471629 + resolution: "@mediapipe/holistic@npm:0.5.1675471629" + checksum: 10c0/241cacfbff10b6418a815dfb24c36d77c626f5e10c60045a3d8cdb9f89c6e346e0d356dcfe1e8bbc4d00040983b4bc426e802a45af3f7142554afda3239ff8fc + languageName: node + linkType: hard + +"@mediapipe/tasks-vision@npm:0.10.9": + version: 0.10.9 + resolution: "@mediapipe/tasks-vision@npm:0.10.9" + checksum: 10c0/38a3112bf5f073e6b15f3a1556c4d628371f1068cbe16aabeae288f43be62f8c17f4ae66590bda11be6a9aeb098736c85d102751277db5bb4b881516a23e43a5 + languageName: node + linkType: hard + +"@mediapipe/tasks-vision@npm:^0.10.18": + version: 0.10.21 + resolution: "@mediapipe/tasks-vision@npm:0.10.21" + checksum: 10c0/11b2bdf98b8cb6e044f2a954e7c8393169e62c86ff49b3d0b61c3b327d18e1ccd47a187999b023bad48380c9da41bfa66eb165301c80da07746390482cb18a19 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -4612,6 +4645,22 @@ __metadata: languageName: node linkType: hard +"@types/dom-mediacapture-transform@npm:^0.1.10": + version: 0.1.11 + resolution: "@types/dom-mediacapture-transform@npm:0.1.11" + dependencies: + "@types/dom-webcodecs": "npm:*" + checksum: 10c0/19c76d54cf31aa2a925011fc5f973dff9a10bdecfdf2285e5e568e61850a0fa2b8c9f1807a1462cbefd57ec26d32eeaa9c359117aca9d9fe7f0d6f2fff33f51e + languageName: node + linkType: hard + +"@types/dom-webcodecs@npm:*": + version: 0.1.14 + resolution: "@types/dom-webcodecs@npm:0.1.14" + checksum: 10c0/20f2e9a52986f861eb5725365561c57d4d8c7494322e55f5df1b0639e97d399c3c427e05d2bc7772c9c975197dc12db3e047e3e0ad7b49ae13181514e3d54f56 + languageName: node + linkType: hard + "@types/estree@npm:1.0.6, @types/estree@npm:^1.0.0": version: 1.0.6 resolution: "@types/estree@npm:1.0.6" @@ -6862,6 +6911,8 @@ __metadata: "@livekit/components-core": "npm:^0.12.0" "@livekit/components-react": "npm:^2.0.0" "@livekit/protocol": "npm:^1.33.0" + "@livekit/track-processors": "npm:^0.3.3" + "@mediapipe/tasks-vision": "npm:^0.10.18" "@opentelemetry/api": "npm:^1.4.0" "@opentelemetry/core": "npm:^1.25.1" "@opentelemetry/exporter-trace-otlp-http": "npm:^0.57.0" @@ -6882,6 +6933,7 @@ __metadata: "@testing-library/react": "npm:^16.0.0" "@testing-library/user-event": "npm:^14.5.1" "@types/content-type": "npm:^1.1.5" + "@types/dom-mediacapture-transform": "npm:^0.1.10" "@types/grecaptcha": "npm:^3.0.9" "@types/jsdom": "npm:^21.1.7" "@types/lodash-es": "npm:^4.17.12" From b7b3fde2023eef04e8bf859f3790696a8bb6ee4b Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 22 Apr 2025 18:00:41 +0200 Subject: [PATCH 33/51] Upgrade LiveKit track processors --- package.json | 2 +- yarn.lock | 28 ++++++++++------------------ 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 413ae6cd..f44264b3 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@livekit/components-core": "^0.12.0", "@livekit/components-react": "^2.0.0", "@livekit/protocol": "^1.33.0", - "@livekit/track-processors": "^0.3.3", + "@livekit/track-processors": "^0.5.5", "@mediapipe/tasks-vision": "^0.10.18", "@opentelemetry/api": "^1.4.0", "@opentelemetry/core": "^1.25.1", diff --git a/yarn.lock b/yarn.lock index 165e6f57..b23ad6ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2536,15 +2536,14 @@ __metadata: languageName: node linkType: hard -"@livekit/track-processors@npm:^0.3.3": - version: 0.3.3 - resolution: "@livekit/track-processors@npm:0.3.3" +"@livekit/track-processors@npm:^0.5.5": + version: 0.5.5 + resolution: "@livekit/track-processors@npm:0.5.5" dependencies: - "@mediapipe/holistic": "npm:0.5.1675471629" - "@mediapipe/tasks-vision": "npm:0.10.9" + "@mediapipe/tasks-vision": "npm:0.10.14" peerDependencies: livekit-client: ^1.12.0 || ^2.1.0 - checksum: 10c0/a33478a340f89ae489ac2e1dcfbc1c69744943301c5f3bd22f604b63b188ef0feebe065d4cf084fcb53d9766d10c7655f8045e19549772471d52d3a976c36778 + checksum: 10c0/b86737c4ce6b93c714dfc814c997d07f1d40b0869b5e269b28e00034a3c06da811d9117c4914d8a2706ac760be31f2e9d0705108c07b29f4bbb4626dcd41ed64 languageName: node linkType: hard @@ -2562,17 +2561,10 @@ __metadata: languageName: node linkType: hard -"@mediapipe/holistic@npm:0.5.1675471629": - version: 0.5.1675471629 - resolution: "@mediapipe/holistic@npm:0.5.1675471629" - checksum: 10c0/241cacfbff10b6418a815dfb24c36d77c626f5e10c60045a3d8cdb9f89c6e346e0d356dcfe1e8bbc4d00040983b4bc426e802a45af3f7142554afda3239ff8fc - languageName: node - linkType: hard - -"@mediapipe/tasks-vision@npm:0.10.9": - version: 0.10.9 - resolution: "@mediapipe/tasks-vision@npm:0.10.9" - checksum: 10c0/38a3112bf5f073e6b15f3a1556c4d628371f1068cbe16aabeae288f43be62f8c17f4ae66590bda11be6a9aeb098736c85d102751277db5bb4b881516a23e43a5 +"@mediapipe/tasks-vision@npm:0.10.14": + version: 0.10.14 + resolution: "@mediapipe/tasks-vision@npm:0.10.14" + checksum: 10c0/dea1d094153fcb91e76d1a266b6a79baf9cbdd106a786adc0236ded87099b5242c89e0e5b73a3f5d57d7e23ff657dd37f40b9261e12a330762041cd9549fadcc languageName: node linkType: hard @@ -6902,7 +6894,7 @@ __metadata: "@livekit/components-core": "npm:^0.12.0" "@livekit/components-react": "npm:^2.0.0" "@livekit/protocol": "npm:^1.33.0" - "@livekit/track-processors": "npm:^0.3.3" + "@livekit/track-processors": "npm:^0.5.5" "@mediapipe/tasks-vision": "npm:^0.10.18" "@opentelemetry/api": "npm:^1.4.0" "@opentelemetry/core": "npm:^1.25.1" From 7142122a742202392f8ddfc53998acd265452698 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 22 Apr 2025 18:05:31 +0200 Subject: [PATCH 34/51] Dedupe Mediapipe dependencies --- package.json | 1 + yarn.lock | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/package.json b/package.json index f44264b3..f091280e 100644 --- a/package.json +++ b/package.json @@ -134,6 +134,7 @@ }, "resolutions": { "@livekit/components-core/rxjs": "^7.8.1", + "@livekit/track-processors/@mediapipe/tasks-vision": "^0.10.18", "matrix-widget-api": "1.11.0" }, "packageManager": "yarn@4.7.0" diff --git a/yarn.lock b/yarn.lock index b23ad6ba..703c9ebf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2561,13 +2561,6 @@ __metadata: languageName: node linkType: hard -"@mediapipe/tasks-vision@npm:0.10.14": - version: 0.10.14 - resolution: "@mediapipe/tasks-vision@npm:0.10.14" - checksum: 10c0/dea1d094153fcb91e76d1a266b6a79baf9cbdd106a786adc0236ded87099b5242c89e0e5b73a3f5d57d7e23ff657dd37f40b9261e12a330762041cd9549fadcc - languageName: node - linkType: hard - "@mediapipe/tasks-vision@npm:^0.10.18": version: 0.10.21 resolution: "@mediapipe/tasks-vision@npm:0.10.21" From 63ad4a3ad316eb4e871461f890709398d2f698ba Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 22 Apr 2025 18:32:01 +0200 Subject: [PATCH 35/51] Get blurring working again --- src/livekit/BlurBackgroundTransformer.ts | 5 +++++ src/livekit/TrackProcessorContext.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/livekit/BlurBackgroundTransformer.ts b/src/livekit/BlurBackgroundTransformer.ts index 51b6a536..d57524fc 100644 --- a/src/livekit/BlurBackgroundTransformer.ts +++ b/src/livekit/BlurBackgroundTransformer.ts @@ -54,9 +54,14 @@ export class BlurBackgroundTransformer extends BackgroundTransformer { delegate: "GPU", ...this.options.segmenterOptions, }, + canvas: this.canvas, runningMode: "VIDEO", outputCategoryMask: true, outputConfidenceMasks: false, }); + + if (this.options.blurRadius) { + this.gl?.setBlurRadius(this.options.blurRadius); + } } } diff --git a/src/livekit/TrackProcessorContext.tsx b/src/livekit/TrackProcessorContext.tsx index 8edec4f5..ba94e257 100644 --- a/src/livekit/TrackProcessorContext.tsx +++ b/src/livekit/TrackProcessorContext.tsx @@ -90,7 +90,7 @@ export const ProcessorProvider: FC = ({ children }) => { try { if (!blur.current) { blur.current = new ProcessorWrapper( - new BlurBackgroundTransformer({}), + new BlurBackgroundTransformer({ blurRadius: 15 }), "background-blur", ); } From 3fc4cc908e8d7e1c605d5996ab1a6c0724f0ae8b Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 22 Apr 2025 19:19:40 +0200 Subject: [PATCH 36/51] Cleanup + use a better check for blurring support --- src/livekit/TrackProcessorContext.tsx | 89 +++++++++------------------ src/livekit/useLiveKit.ts | 2 +- src/room/LobbyView.tsx | 2 +- src/settings/SettingsModal.tsx | 7 +-- 4 files changed, 33 insertions(+), 67 deletions(-) diff --git a/src/livekit/TrackProcessorContext.tsx b/src/livekit/TrackProcessorContext.tsx index ba94e257..b310e7b3 100644 --- a/src/livekit/TrackProcessorContext.tsx +++ b/src/livekit/TrackProcessorContext.tsx @@ -7,18 +7,10 @@ Please see LICENSE in the repository root for full details. import { ProcessorWrapper, + supportsBackgroundProcessors, type BackgroundOptions, } from "@livekit/track-processors"; -import { - createContext, - type FC, - useCallback, - useContext, - useEffect, - useRef, - useState, -} from "react"; -import { logger } from "matrix-js-sdk/src/logger"; +import { createContext, type FC, useContext, useEffect, useMemo } from "react"; import { type LocalVideoTrack } from "livekit-client"; import { @@ -30,23 +22,23 @@ import { BlurBackgroundTransformer } from "./BlurBackgroundTransformer"; type ProcessorState = { supported: boolean | undefined; processor: undefined | ProcessorWrapper; - /** - * Call this method to try to initialize a processor. - * This only needs to happen if supported is undefined. - * If the backgroundBlur setting is set to true this does not need to be called - * and the processorState.supported will update automatically to the correct value. - */ - checkSupported: () => void; }; + const ProcessorContext = createContext(undefined); -export const useTrackProcessor = (): ProcessorState | undefined => - useContext(ProcessorContext); +export function useTrackProcessor(): ProcessorState { + const state = useContext(ProcessorContext); + if (state === undefined) + throw new Error( + "useTrackProcessor must be used within a ProcessorProvider", + ); + return state; +} export const useTrackProcessorSync = ( videoTrack: LocalVideoTrack | null, ): void => { - const { processor } = useTrackProcessor() || {}; + const { processor } = useTrackProcessor(); useEffect(() => { if (!videoTrack) return; if (processor && !videoTrack.getProcessor()) { @@ -61,53 +53,28 @@ export const useTrackProcessorSync = ( interface Props { children: JSX.Element; } + export const ProcessorProvider: FC = ({ children }) => { // The setting the user wants to have const [blurActivated] = useSetting(backgroundBlurSettings); - - // If `ProcessorState.supported` is undefined the user can activate that we want - // to have it at least checked (this is useful to show the settings menu properly) - // We dont want to try initializing the blur if the user is not even looking at the setting - const [shouldCheckSupport, setShouldCheckSupport] = useState(blurActivated); - - // Cache the processor so we only need to initialize it once. - const blur = useRef | undefined>( - undefined, + const supported = useMemo(() => supportsBackgroundProcessors(), []); + const blur = useMemo( + () => + new ProcessorWrapper( + new BlurBackgroundTransformer({ blurRadius: 15 }), + "background-blur", + ), + [], ); - const checkSupported = useCallback(() => { - setShouldCheckSupport(true); - }, []); // This is the actual state exposed through the context - const [processorState, setProcessorState] = useState(() => ({ - supported: false, - processor: undefined, - checkSupported, - })); - - useEffect(() => { - if (!shouldCheckSupport) return; - try { - if (!blur.current) { - blur.current = new ProcessorWrapper( - new BlurBackgroundTransformer({ blurRadius: 15 }), - "background-blur", - ); - } - setProcessorState({ - checkSupported, - supported: true, - processor: blurActivated ? blur.current : undefined, - }); - } catch (e) { - setProcessorState({ - checkSupported, - supported: false, - processor: undefined, - }); - logger.error("disable background blur", e); - } - }, [blurActivated, checkSupported, shouldCheckSupport]); + const processorState = useMemo( + () => ({ + supported, + processor: supported && blurActivated ? blur : undefined, + }), + [supported, blurActivated, blur], + ); return ( diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index c9bfd78e..f214a203 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -88,7 +88,7 @@ export function useLiveKit( const devices = useMediaDevices(); const initialDevices = useRef(devices); - const { processor } = useTrackProcessor() || {}; + const { processor } = useTrackProcessor(); const initialProcessor = useInitial(() => processor); const roomOptions = useMemo( (): RoomOptions => ({ diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index f3739175..e6bf5362 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -120,7 +120,7 @@ export const LobbyView: FC = ({ muteStates.audio.enabled && { deviceId: devices.audioInput.selectedId }, ); - const { processor } = useTrackProcessor() || {}; + const { processor } = useTrackProcessor(); const initialProcessor = useInitial(() => processor); const localTrackOptions = useMemo( diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 44d06116..b24674dc 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -5,10 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { type FC, type ReactNode, useEffect, useState } from "react"; +import { type FC, type ReactNode, useState } from "react"; import { useTranslation } from "react-i18next"; import { type MatrixClient } from "matrix-js-sdk"; -import { Root as Form ,Separator} from "@vector-im/compound-web"; +import { Root as Form, Separator } from "@vector-im/compound-web"; import { type Room as LivekitRoom } from "livekit-client"; import { Modal } from "../Modal"; @@ -69,8 +69,7 @@ export const SettingsModal: FC = ({ // Generate a `Checkbox` input to turn blur on or off. const BlurCheckbox: React.FC = (): ReactNode => { - const { supported, checkSupported } = useTrackProcessor() || {}; - useEffect(() => checkSupported?.(), [checkSupported]); + const { supported } = useTrackProcessor(); const [blurActive, setBlurActive] = useSetting(backgroundBlurSetting); From de4875ac7ccdf82bd62d7a9a1148865d275a9d98 Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 25 Apr 2025 00:31:13 +0200 Subject: [PATCH 37/51] replace jsonc with nginx highlighting --- docs/self-hosting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/self-hosting.md b/docs/self-hosting.md index 0e3e8a82..4946c3a1 100644 --- a/docs/self-hosting.md +++ b/docs/self-hosting.md @@ -93,7 +93,7 @@ the example above, this results in: Using Nginx, you can achieve this by: -```jsonc +```nginx configuration file` server { ... location ^~ /livekit/jwt/ { @@ -203,7 +203,7 @@ Because Element Call uses client-side routing, your server must be able to route any requests to non-existing paths back to `/index.html`. For example, in Nginx you can achieve this with the `try_files` directive: -```jsonc +```nginx configuration file server { ... location / { From 984383b60d2fbe7ad40487975bd7dfd9871903fd Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 25 Apr 2025 00:32:19 +0200 Subject: [PATCH 38/51] typo --- docs/self-hosting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/self-hosting.md b/docs/self-hosting.md index 4946c3a1..85ace615 100644 --- a/docs/self-hosting.md +++ b/docs/self-hosting.md @@ -93,7 +93,7 @@ the example above, this results in: Using Nginx, you can achieve this by: -```nginx configuration file` +```nginx configuration file server { ... location ^~ /livekit/jwt/ { From c91cc79162ddde77275a189f0d9d9c9a76d76e41 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 28 Apr 2025 15:56:26 +0200 Subject: [PATCH 39/51] Remove unneeded Knip override for MediaPipe --- knip.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/knip.ts b/knip.ts index 4732aea3..f1189ca8 100644 --- a/knip.ts +++ b/knip.ts @@ -32,8 +32,6 @@ export default { // element call side. "@types/dom-mediacapture-transform", "matrix-widget-api", - // we bundle files from this and lazy load them: - "@mediapipe/tasks-vision", ], ignoreExportsUsedInFile: true, } satisfies KnipConfig; From e88e0b19019089ab63c82b17e017d9d6c99ef343 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 28 Apr 2025 15:58:50 +0200 Subject: [PATCH 40/51] Import the segmenter model in a more statically-analyzable way --- src/livekit/BlurBackgroundTransformer.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/livekit/BlurBackgroundTransformer.ts b/src/livekit/BlurBackgroundTransformer.ts index d57524fc..0ff363c2 100644 --- a/src/livekit/BlurBackgroundTransformer.ts +++ b/src/livekit/BlurBackgroundTransformer.ts @@ -1,5 +1,5 @@ /* -Copyright 2024 New Vector Ltd. +Copyright 2024-2025 New Vector Ltd. SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. @@ -12,6 +12,8 @@ import { } from "@livekit/track-processors"; import { ImageSegmenter } from "@mediapipe/tasks-vision"; +import modelAssetPath from "../mediapipe/imageSegmenter/selfie_segmenter.tflite?url"; + interface WasmFileset { /** The path to the Wasm loader script. */ wasmLoaderPath: string; @@ -32,11 +34,6 @@ const wasmFileset: WasmFileset = { ).href, }; -const modelAssetPath = new URL( - "../mediapipe/imageSegmenter/selfie_segmenter.tflite", - import.meta.url, -).href; - export class BlurBackgroundTransformer extends BackgroundTransformer { public async init({ outputCanvas, From 02ce1f701aed930c1e3dbd7e9c65d499c5124cb0 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 28 Apr 2025 15:59:32 +0200 Subject: [PATCH 41/51] Document the background transformer a bit better --- src/livekit/BlurBackgroundTransformer.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/livekit/BlurBackgroundTransformer.ts b/src/livekit/BlurBackgroundTransformer.ts index 0ff363c2..72256fe1 100644 --- a/src/livekit/BlurBackgroundTransformer.ts +++ b/src/livekit/BlurBackgroundTransformer.ts @@ -21,8 +21,16 @@ interface WasmFileset { wasmBinaryPath: string; } -// n.b. this only includes the SIMD versions of the WASM files which have good support: -// https://caniuse.com/?search=simd +// The MediaPipe package, by default, ships some alternative versions of the +// WASM files which avoid SIMD for compatibility with older browsers. But SIMD +// in WASM is actually fine by our support policy, so we include just the SIMD +// versions. +// It's really not ideal that we have to reference these internal files from +// MediaPipe and depend on node_modules having this specific structure. It's +// easy to see this breaking if our dependencies changed and MediaPipe were +// no longer hoisted, or if we switched to another dependency loader such as +// Yarn PnP. +// https://github.com/google-ai-edge/mediapipe/issues/5961 const wasmFileset: WasmFileset = { wasmLoaderPath: new URL( "../../node_modules/@mediapipe/tasks-vision/wasm/vision_wasm_internal.js", @@ -34,12 +42,20 @@ const wasmFileset: WasmFileset = { ).href, }; +/** + * Track processor that applies effects such as blurring to a user's background. + * + * This is just like LiveKit's prebuilt BackgroundTransformer except that it + * loads the segmentation models from our own bundle rather than as an external + * resource fetched from the public internet. + */ export class BlurBackgroundTransformer extends BackgroundTransformer { public async init({ outputCanvas, inputElement: inputVideo, }: VideoTransformerInitOptions): Promise { - // call super.super.init() + // Call super.super.init() since we're totally replacing the init method of + // BackgroundTransformer here, rather than extending it await VideoTransformer.prototype.init.call(this, { outputCanvas, inputElement: inputVideo, From cd9035b5fca34ed6cfaea038164f4e52d6c9cfba Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 28 Apr 2025 16:00:03 +0200 Subject: [PATCH 42/51] Make background blurring actually react to track restarts --- src/livekit/useLiveKit.ts | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index f214a203..99eda021 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -9,7 +9,7 @@ import { ConnectionState, type E2EEManagerOptions, ExternalE2EEKeyProvider, - type LocalVideoTrack, + LocalVideoTrack, Room, type RoomOptions, Track, @@ -18,6 +18,8 @@ import { useEffect, useMemo, useRef } from "react"; import E2EEWorker from "livekit-client/e2ee-worker?worker"; import { logger } from "matrix-js-sdk/lib/logger"; import { type MatrixRTCSession } from "matrix-js-sdk/lib/matrixrtc"; +import { useObservable, useObservableEagerState } from "observable-hooks"; +import { map } from "rxjs"; import { defaultLiveKitOptions } from "./options"; import { type SFUConfig } from "./openIDSFU"; @@ -39,6 +41,7 @@ import { useTrackProcessorSync, } from "./TrackProcessorContext"; import { useInitial } from "../useInitial"; +import { observeTrackReference$ } from "../state/MediaViewModel"; interface UseLivekitResult { livekitRoom?: Room; @@ -132,21 +135,25 @@ export function useLiveKit( return r; }, [roomOptions, e2eeSystem]); - const videoTrack = useMemo( - () => - Array.from(room.localParticipant.videoTrackPublications.values()).find( - (v) => v.source === Track.Source.Camera, - )?.track as LocalVideoTrack | null, - [ - room.localParticipant.videoTrackPublications, - // We need to update on map changes - // eslint-disable-next-line react-hooks/exhaustive-deps - room.localParticipant.videoTrackPublications.keys(), - ], + // Sync the requested track processors with LiveKit + useTrackProcessorSync( + useObservableEagerState( + useObservable( + (room$) => + observeTrackReference$( + room$.pipe(map(([room]) => room.localParticipant)), + Track.Source.Camera, + ).pipe( + map((trackRef) => { + const track = trackRef?.publication?.track; + return track instanceof LocalVideoTrack ? track : null; + }), + ), + [room], + ), + ), ); - useTrackProcessorSync(videoTrack); - const connectionState = useECConnectionState( { deviceId: initialDevices.current.audioInput.selectedId, From 7d0117903ebff92748988b43d0108571bac9e9d0 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 28 Apr 2025 16:01:34 +0200 Subject: [PATCH 43/51] Don't lie about the type of the video track --- src/room/LobbyView.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index e6bf5362..0cabc645 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -156,10 +156,12 @@ export const LobbyView: FC = ({ const tracks = usePreviewTracks(localTrackOptions, onError); - const videoTrack = useMemo(() => { - const track = tracks?.find((t) => t.kind === Track.Kind.Video); - return track as LocalVideoTrack | null; - }, [tracks]); + const videoTrack = useMemo( + () => + (tracks?.find((t) => t.kind === Track.Kind.Video) ?? + null) as LocalVideoTrack | null, + [tracks], + ); useTrackProcessorSync(videoTrack); const showSwitchCamera = useShowSwitchCamera( useObservable( From 8f1761442b2e554b39c33ba0ab2604a24e83c5d1 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 28 Apr 2025 16:37:53 +0200 Subject: [PATCH 44/51] Fix tests --- src/room/GroupCallView.test.tsx | 34 +++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/room/GroupCallView.test.tsx b/src/room/GroupCallView.test.tsx index 76765270..75961c72 100644 --- a/src/room/GroupCallView.test.tsx +++ b/src/room/GroupCallView.test.tsx @@ -38,6 +38,7 @@ import { GroupCallView } from "./GroupCallView"; import { type WidgetHelpers } from "../widget"; import { LazyEventEmitter } from "../LazyEventEmitter"; import { MatrixRTCFocusMissingError } from "../utils/errors"; +import { ProcessorProvider } from "../livekit/TrackProcessorContext"; vi.mock("../soundUtils"); vi.mock("../useAudioContext"); @@ -46,6 +47,13 @@ vi.mock("react-use-measure", () => ({ default: (): [() => void, object] => [(): void => {}, {}], })); +vi.hoisted( + () => + (global.ImageData = class MockImageData { + public data: number[] = []; + } as unknown as typeof ImageData), +); + const enterRTCSession = vi.hoisted(() => vi.fn(async () => Promise.resolve())); const leaveRTCSession = vi.hoisted(() => vi.fn( @@ -137,18 +145,20 @@ function createGroupCallView( const { getByText } = render( - + + + , ); From 0b82dcf701d4df6d91b6523c7dd7b71be1646510 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 28 Apr 2025 17:44:22 +0200 Subject: [PATCH 45/51] Upgrade TypeScript for ImageBufferSource support --- package.json | 6 +-- yarn.lock | 134 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 100 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 0650d263..2b570028 100644 --- a/package.json +++ b/package.json @@ -73,8 +73,8 @@ "@types/react-dom": "^18.3.0", "@types/sdp-transform": "^2.4.5", "@types/uuid": "10", - "@typescript-eslint/eslint-plugin": "^8.0.0", - "@typescript-eslint/parser": "^8.0.0", + "@typescript-eslint/eslint-plugin": "^8.31.0", + "@typescript-eslint/parser": "^8.31.0", "@use-gesture/react": "^10.2.11", "@vector-im/compound-design-tokens": "^3.0.0", "@vector-im/compound-web": "^7.2.0", @@ -121,7 +121,7 @@ "react-use-measure": "^2.1.1", "rxjs": "^7.8.1", "sass": "^1.42.1", - "typescript": "^5.1.6", + "typescript": "^5.8.3", "typescript-eslint-language-service": "^5.0.5", "unique-names-generator": "^4.6.0", "vaul": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index ab0d017f..d7a49a1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4831,15 +4831,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^8.0.0": - version: 8.25.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.25.0" +"@typescript-eslint/eslint-plugin@npm:^8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.31.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.25.0" - "@typescript-eslint/type-utils": "npm:8.25.0" - "@typescript-eslint/utils": "npm:8.25.0" - "@typescript-eslint/visitor-keys": "npm:8.25.0" + "@typescript-eslint/scope-manager": "npm:8.31.0" + "@typescript-eslint/type-utils": "npm:8.31.0" + "@typescript-eslint/utils": "npm:8.31.0" + "@typescript-eslint/visitor-keys": "npm:8.31.0" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -4847,8 +4847,8 @@ __metadata: peerDependencies: "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <5.8.0" - checksum: 10c0/11d63850f5f03b29cd31166f8da111788dc74e46877c2e16a5c488d6c4aa4b6c68c0857b9a396ad920aa7f0f3e7166f4faecbb194c19cd2bb9d3f687c5d2b292 + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/7d78e0cdcc967742752d49d2d38986ee38d0b7ca64af247e5fe0816cea9ae5f1bfa5c126154acc0846af515c4fb1c52c96926ee25c73b4c3f7e6fd73cb6d2b0e languageName: node linkType: hard @@ -4863,19 +4863,19 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^8.0.0": - version: 8.25.0 - resolution: "@typescript-eslint/parser@npm:8.25.0" +"@typescript-eslint/parser@npm:^8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/parser@npm:8.31.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.25.0" - "@typescript-eslint/types": "npm:8.25.0" - "@typescript-eslint/typescript-estree": "npm:8.25.0" - "@typescript-eslint/visitor-keys": "npm:8.25.0" + "@typescript-eslint/scope-manager": "npm:8.31.0" + "@typescript-eslint/types": "npm:8.31.0" + "@typescript-eslint/typescript-estree": "npm:8.31.0" + "@typescript-eslint/visitor-keys": "npm:8.31.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <5.8.0" - checksum: 10c0/9a54539ba297791f23093ff42a885cc57d36b26205d7a390e114d1f01cc584ce91ac6ead01819daa46b48f873cac6c829fcf399a436610bdbfa98e5cd78148a2 + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/9bd903b3ea4e24bfeb444d7a5c2ed82e591ef5cffc0874c609de854c05d34935cd85543e66678ecdb8e0e3eae2cda2df5c1ba66eb72010632cb9f8779031d56d languageName: node linkType: hard @@ -4899,18 +4899,28 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.25.0": - version: 8.25.0 - resolution: "@typescript-eslint/type-utils@npm:8.25.0" +"@typescript-eslint/scope-manager@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/scope-manager@npm:8.31.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.25.0" - "@typescript-eslint/utils": "npm:8.25.0" + "@typescript-eslint/types": "npm:8.31.0" + "@typescript-eslint/visitor-keys": "npm:8.31.0" + checksum: 10c0/eae758a24cc578fa351b8bf0c30c50de384292c0b05a58762f9b632d65a009bd5d902d806eccb6b678cc0b09686289fb4f1fd67da7f12d59ad43ff033b35cc4f + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/type-utils@npm:8.31.0" + dependencies: + "@typescript-eslint/typescript-estree": "npm:8.31.0" + "@typescript-eslint/utils": "npm:8.31.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^2.0.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <5.8.0" - checksum: 10c0/b7477a2d239cfd337f7d28641666763cf680a43a8d377a09dc42415f715670d35fbb4e772e103dfe8cd620c377e66bce740106bb3983ee65a739c28fab7325d1 + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/f6938413a583430468b259f6823bb2ab1b5cd77cd6d4e21e1803df70e329046b9579aed5bdc9bdcf4046c8091615a911ac3990859db78d00210bb867915ba37f languageName: node linkType: hard @@ -4928,6 +4938,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/types@npm:8.31.0" + checksum: 10c0/04130a30aac477d36d6a155399b27773457aeb9b485ef8fb56fee05725b6e36768c9fac7e4d1f073fd16988de0eb7dffc743c3f834ae907cf918cabb075e5cd8 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" @@ -4964,6 +4981,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.31.0" + dependencies: + "@typescript-eslint/types": "npm:8.31.0" + "@typescript-eslint/visitor-keys": "npm:8.31.0" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^2.0.1" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/0ec074b2b9c49f80fafea716aa0cc4b05085e65730a3ef7c7d2d39db1657a40b38abe83f22bbe15ac4f6fdf576692f47d2d057347242e6cef5be81d070f55064 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/utils@npm:5.62.0" @@ -4982,7 +5017,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.25.0, @typescript-eslint/utils@npm:^8.13.0": +"@typescript-eslint/utils@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/utils@npm:8.31.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" + "@typescript-eslint/scope-manager": "npm:8.31.0" + "@typescript-eslint/types": "npm:8.31.0" + "@typescript-eslint/typescript-estree": "npm:8.31.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/1fd4f62e16a44a5be2de501f70ba4b2d64479e014370bde7bbc6de6897cf1699766a8b7be4deb9b0328e74c2b4171839336ede4e3c60fec6ac8378b623a75275 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:^8.13.0": version: 8.25.0 resolution: "@typescript-eslint/utils@npm:8.25.0" dependencies: @@ -5017,6 +5067,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.31.0" + dependencies: + "@typescript-eslint/types": "npm:8.31.0" + eslint-visitor-keys: "npm:^4.2.0" + checksum: 10c0/e41e2a9e287d11232cda6126377d1df4de69c6e9dc2a14058819cff15280ec654a3877886a6806728196f299766cfbb0b299eb021c2ce168eb15dff5eb07b51b + languageName: node + linkType: hard + "@ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" @@ -6920,8 +6980,8 @@ __metadata: "@types/react-dom": "npm:^18.3.0" "@types/sdp-transform": "npm:^2.4.5" "@types/uuid": "npm:10" - "@typescript-eslint/eslint-plugin": "npm:^8.0.0" - "@typescript-eslint/parser": "npm:^8.0.0" + "@typescript-eslint/eslint-plugin": "npm:^8.31.0" + "@typescript-eslint/parser": "npm:^8.31.0" "@use-gesture/react": "npm:^10.2.11" "@vector-im/compound-design-tokens": "npm:^3.0.0" "@vector-im/compound-web": "npm:^7.2.0" @@ -6968,7 +7028,7 @@ __metadata: react-use-measure: "npm:^2.1.1" rxjs: "npm:^7.8.1" sass: "npm:^1.42.1" - typescript: "npm:^5.1.6" + typescript: "npm:^5.8.3" typescript-eslint-language-service: "npm:^5.0.5" unique-names-generator: "npm:^4.6.0" vaul: "npm:^1.0.0" @@ -12709,13 +12769,13 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.1.6": - version: 5.7.3 - resolution: "typescript@npm:5.7.3" +"typescript@npm:^5.8.3": + version: 5.8.3 + resolution: "typescript@npm:5.8.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/b7580d716cf1824736cc6e628ab4cd8b51877408ba2be0869d2866da35ef8366dd6ae9eb9d0851470a39be17cbd61df1126f9e211d8799d764ea7431d5435afa + checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 languageName: node linkType: hard @@ -12729,13 +12789,13 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.1.6#optional!builtin": - version: 5.7.3 - resolution: "typescript@patch:typescript@npm%3A5.7.3#optional!builtin::version=5.7.3&hash=5786d5" +"typescript@patch:typescript@npm%3A^5.8.3#optional!builtin": + version: 5.8.3 + resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/6fd7e0ed3bf23a81246878c613423730c40e8bdbfec4c6e4d7bf1b847cbb39076e56ad5f50aa9d7ebd89877999abaee216002d3f2818885e41c907caaa192cc4 + checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb languageName: node linkType: hard From 9df0f581178885d50dfbcce40168348e9b6c1dc7 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 29 Apr 2025 12:04:08 +0200 Subject: [PATCH 46/51] Inline a patched version of WebCodecs types --- knip.ts | 4 - package.json | 1 - src/@types/dom-mediacapture-transform.d.ts | 137 ++++ src/@types/dom-webcodecs.d.ts | 736 +++++++++++++++++++++ yarn.lock | 17 - 5 files changed, 873 insertions(+), 22 deletions(-) create mode 100644 src/@types/dom-mediacapture-transform.d.ts create mode 100644 src/@types/dom-webcodecs.d.ts diff --git a/knip.ts b/knip.ts index f1189ca8..05bd029d 100644 --- a/knip.ts +++ b/knip.ts @@ -27,10 +27,6 @@ export default { // then Knip will flag it as a false positive // https://github.com/webpro-nl/knip/issues/766 "@vector-im/compound-web", - // We need this so the eslint is happy with @livekit/track-processors. - // This might be a bug in the livekit repo but for now we fix it on the - // element call side. - "@types/dom-mediacapture-transform", "matrix-widget-api", ], ignoreExportsUsedInFile: true, diff --git a/package.json b/package.json index 2b570028..36c9c9cf 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,6 @@ "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.1", "@types/content-type": "^1.1.5", - "@types/dom-mediacapture-transform": "^0.1.10", "@types/grecaptcha": "^3.0.9", "@types/jsdom": "^21.1.7", "@types/lodash-es": "^4.17.12", diff --git a/src/@types/dom-mediacapture-transform.d.ts b/src/@types/dom-mediacapture-transform.d.ts new file mode 100644 index 00000000..ac55dc90 --- /dev/null +++ b/src/@types/dom-mediacapture-transform.d.ts @@ -0,0 +1,137 @@ +/* eslint-disable */ +// The contents of this file below the line are copied from +// @types/dom-mediacapture-transform, which is inlined here into Element Call so +// that we can apply the patch to @types/dom-webcodecs found in +// ./dom-webcodecs.d.ts, which it depends on. +// (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/72625) +// Once that PR is merged and released, we can remove this file and return to +// depending on @types/dom-mediacapture-transform. +// ----------------------------------------------------------------------------- + +// In general, these types are only available behind a command line flag or an origin trial in +// Chrome 90+. + +// This API depends on WebCodecs. + +// Versioning: +// Until the above-mentioned spec is finalized, the major version number is 0. Although not +// necessary for version 0, consider incrementing the minor version number for breaking changes. + +// The following modify existing DOM types to allow defining type-safe APIs on audio and video tracks. + +/** Specialize MediaStreamTrack so that we can refer specifically to an audio track. */ +interface MediaStreamAudioTrack extends MediaStreamTrack { + readonly kind: "audio"; + clone(): MediaStreamAudioTrack; +} + +/** Specialize MediaStreamTrack so that we can refer specifically to a video track. */ +interface MediaStreamVideoTrack extends MediaStreamTrack { + readonly kind: "video"; + clone(): MediaStreamVideoTrack; +} + +/** Assert that getAudioTracks and getVideoTracks return the tracks with the appropriate kind. */ +interface MediaStream { + getAudioTracks(): MediaStreamAudioTrack[]; + getVideoTracks(): MediaStreamVideoTrack[]; +} + +// The following were originally generated from the spec using +// https://github.com/microsoft/TypeScript-DOM-lib-generator, then heavily modified. + +/** + * A track sink that is capable of exposing the unencoded frames from the track to a + * ReadableStream, and exposes a control channel for signals going in the oppposite direction. + */ +interface MediaStreamTrackProcessor { + /** + * Allows reading the frames flowing through the MediaStreamTrack provided to the constructor. + */ + readonly readable: ReadableStream; + /** Allows sending control signals to the MediaStreamTrack provided to the constructor. */ + readonly writableControl: WritableStream; +} + +declare var MediaStreamTrackProcessor: { + prototype: MediaStreamTrackProcessor; + + /** Constructor overrides based on the type of track. */ + new ( + init: MediaStreamTrackProcessorInit & { track: MediaStreamAudioTrack }, + ): MediaStreamTrackProcessor; + new ( + init: MediaStreamTrackProcessorInit & { track: MediaStreamVideoTrack }, + ): MediaStreamTrackProcessor; +}; + +interface MediaStreamTrackProcessorInit { + track: MediaStreamTrack; + /** + * If media frames are not read from MediaStreamTrackProcessor.readable quickly enough, the + * MediaStreamTrackProcessor will internally buffer up to maxBufferSize of the frames produced + * by the track. If the internal buffer is full, each time the track produces a new frame, the + * oldest frame in the buffer will be dropped and the new frame will be added to the buffer. + */ + maxBufferSize?: number | undefined; +} + +/** + * Takes video frames as input, and emits control signals that result from subsequent processing. + */ +interface MediaStreamTrackGenerator + extends MediaStreamTrack { + /** + * Allows writing media frames to the MediaStreamTrackGenerator, which is itself a + * MediaStreamTrack. When a frame is written to writable, the frame’s close() method is + * automatically invoked, so that its internal resources are no longer accessible from + * JavaScript. + */ + readonly writable: WritableStream; + /** + * Allows reading control signals sent from any sinks connected to the + * MediaStreamTrackGenerator. + */ + readonly readableControl: ReadableStream; +} + +type MediaStreamAudioTrackGenerator = MediaStreamTrackGenerator & + MediaStreamAudioTrack; +type MediaStreamVideoTrackGenerator = MediaStreamTrackGenerator & + MediaStreamVideoTrack; + +declare var MediaStreamTrackGenerator: { + prototype: MediaStreamTrackGenerator; + + /** Constructor overrides based on the type of track. */ + new ( + init: MediaStreamTrackGeneratorInit & { + kind: "audio"; + signalTarget?: MediaStreamAudioTrack | undefined; + }, + ): MediaStreamAudioTrackGenerator; + new ( + init: MediaStreamTrackGeneratorInit & { + kind: "video"; + signalTarget?: MediaStreamVideoTrack | undefined; + }, + ): MediaStreamVideoTrackGenerator; +}; + +interface MediaStreamTrackGeneratorInit { + kind: MediaStreamTrackGeneratorKind; + /** + * (Optional) track to which the MediaStreamTrackGenerator will automatically forward control + * signals. If signalTarget is provided and signalTarget.kind and kind do not match, the + * MediaStreamTrackGenerator’s constructor will raise an exception. + */ + signalTarget?: MediaStreamTrack | undefined; +} + +type MediaStreamTrackGeneratorKind = "audio" | "video"; + +type MediaStreamTrackSignalType = "request-frame"; + +interface MediaStreamTrackSignal { + signalType: MediaStreamTrackSignalType; +} diff --git a/src/@types/dom-webcodecs.d.ts b/src/@types/dom-webcodecs.d.ts new file mode 100644 index 00000000..f06ceedd --- /dev/null +++ b/src/@types/dom-webcodecs.d.ts @@ -0,0 +1,736 @@ +/* eslint-disable */ +// The contents of this file below the line are copied from +// @types/dom-webcodecs, which is inlined here into Element Call so that we can +// apply the patch https://github.com/DefinitelyTyped/DefinitelyTyped/pull/72625 +// which is needed for TypeScript 5.8 compatibility. Once that PR is merged and +// released, we can remove this file and return to depending on +// @types/dom-webcodecs. +// ----------------------------------------------------------------------------- + +// Versioning: +// Until the WebCodecs spec is finalized, the major version number is 0. I have chosen to use minor +// version 1 to denote the API as defined by the IDL files from the Chromium repo at +// https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/webcodecs. +// Please use a version number above 0.1 if using the spec at https://w3c.github.io/webcodecs/ as +// the source. + +// The declarations in webcodecs.generated.d.ts have been generated using the code in +// https://github.com/yume-chan/webcodecs-lib-generator. See +// https://github.com/yume-chan/webcodecs-lib-generator/blob/main/README.md for more detail. + +// The following declarations are copied from +// https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/a75338e1ea8a958bf08a5745141d2ab8f14ba2ca/baselines/dom.generated.d.ts +// and modified to expand the types to include VideoFrame. + +/** Shim for OffscreenCanvas, which was removed in TS 4.4 */ +interface OffscreenCanvas extends EventTarget {} + +/** + * Replaces CanvasImageSource; only applies if WebCodecs is available. + */ +type CanvasImageSourceWebCodecs = + | HTMLOrSVGImageElement + | HTMLVideoElement + | HTMLCanvasElement + | ImageBitmap + | OffscreenCanvas + | VideoFrame; + +interface CanvasRenderingContext2D { + drawImage(image: CanvasImageSourceWebCodecs, dx: number, dy: number): void; + drawImage( + image: CanvasImageSourceWebCodecs, + dx: number, + dy: number, + dw: number, + dh: number, + ): void; + drawImage( + image: CanvasImageSourceWebCodecs, + sx: number, + sy: number, + sw: number, + sh: number, + dx: number, + dy: number, + dw: number, + dh: number, + ): void; + createPattern( + image: CanvasImageSourceWebCodecs, + repetition: string | null, + ): CanvasPattern | null; +} + +interface OffscreenCanvasRenderingContext2D { + drawImage(image: CanvasImageSourceWebCodecs, dx: number, dy: number): void; + drawImage( + image: CanvasImageSourceWebCodecs, + dx: number, + dy: number, + dw: number, + dh: number, + ): void; + drawImage( + image: CanvasImageSourceWebCodecs, + sx: number, + sy: number, + sw: number, + sh: number, + dx: number, + dy: number, + dw: number, + dh: number, + ): void; + createPattern( + image: CanvasImageSourceWebCodecs, + repetition: string | null, + ): CanvasPattern | null; +} + +/** + * Replaces ImageBitmapSource; only applies if WebCodecs is available. + */ +type ImageBitmapSourceWebCodecs = CanvasImageSourceWebCodecs | Blob | ImageData; + +declare function createImageBitmap( + image: ImageBitmapSourceWebCodecs, + options?: ImageBitmapOptions, +): Promise; +declare function createImageBitmap( + image: ImageBitmapSourceWebCodecs, + sx: number, + sy: number, + sw: number, + sh: number, + options?: ImageBitmapOptions, +): Promise; + +/** + * Replaces TexImageSource; only applies if WebCodecs is available. + */ +type TexImageSourceWebCodecs = + | ImageBitmap + | ImageData + | HTMLImageElement + | HTMLCanvasElement + | HTMLVideoElement + | OffscreenCanvas + | VideoFrame; + +interface WebGLRenderingContextOverloads { + texImage2D( + target: GLenum, + level: GLint, + internalformat: GLint, + format: GLenum, + type: GLenum, + source: TexImageSourceWebCodecs, + ): void; + texSubImage2D( + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + format: GLenum, + type: GLenum, + source: TexImageSourceWebCodecs, + ): void; +} + +interface WebGL2RenderingContextBase { + texImage3D( + target: GLenum, + level: GLint, + internalformat: GLint, + width: GLsizei, + height: GLsizei, + depth: GLsizei, + border: GLint, + format: GLenum, + type: GLenum, + source: TexImageSourceWebCodecs, + ): void; + texSubImage3D( + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + zoffset: GLint, + width: GLsizei, + height: GLsizei, + depth: GLsizei, + format: GLenum, + type: GLenum, + source: TexImageSourceWebCodecs, + ): void; +} + +interface WebGL2RenderingContextOverloads { + texImage2D( + target: GLenum, + level: GLint, + internalformat: GLint, + format: GLenum, + type: GLenum, + source: TexImageSourceWebCodecs, + ): void; + texImage2D( + target: GLenum, + level: GLint, + internalformat: GLint, + width: GLsizei, + height: GLsizei, + border: GLint, + format: GLenum, + type: GLenum, + source: TexImageSourceWebCodecs, + ): void; + texSubImage2D( + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + format: GLenum, + type: GLenum, + source: TexImageSourceWebCodecs, + ): void; + texSubImage2D( + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + type: GLenum, + source: TexImageSourceWebCodecs, + ): void; +} + +///////////////////////////// +/// webcodecs APIs +///////////////////////////// + +interface AudioDataCopyToOptions { + format?: AudioSampleFormat | undefined; + frameCount?: number | undefined; + frameOffset?: number | undefined; + planeIndex: number; +} + +interface AudioDataInit { + data: AllowSharedBufferSource; + format: AudioSampleFormat; + numberOfChannels: number; + numberOfFrames: number; + sampleRate: number; + timestamp: number; +} + +interface AudioDecoderConfig { + codec: string; + description?: AllowSharedBufferSource | undefined; + numberOfChannels: number; + sampleRate: number; +} + +interface AudioDecoderInit { + error: WebCodecsErrorCallback; + output: AudioDataOutputCallback; +} + +interface AudioDecoderSupport { + config?: AudioDecoderConfig; + supported?: boolean; +} + +interface AudioEncoderConfig { + bitrate?: number | undefined; + codec: string; + numberOfChannels: number; + sampleRate: number; +} + +interface AudioEncoderInit { + error: WebCodecsErrorCallback; + output: EncodedAudioChunkOutputCallback; +} + +interface AudioEncoderSupport { + config?: AudioEncoderConfig; + supported?: boolean; +} + +interface AvcEncoderConfig { + format?: AvcBitstreamFormat | undefined; +} + +interface EncodedAudioChunkInit { + data: AllowSharedBufferSource; + duration?: number | undefined; + timestamp: number; + type: EncodedAudioChunkType; +} + +interface EncodedAudioChunkMetadata { + decoderConfig?: AudioDecoderConfig | undefined; +} + +interface EncodedVideoChunkInit { + data: AllowSharedBufferSource; + duration?: number | undefined; + timestamp: number; + type: EncodedVideoChunkType; +} + +interface EncodedVideoChunkMetadata { + decoderConfig?: VideoDecoderConfig | undefined; + temporalLayerId?: number | undefined; +} + +interface ImageDecodeOptions { + completeFramesOnly?: boolean | undefined; + frameIndex?: number | undefined; +} + +interface ImageDecodeResult { + complete: boolean; + image: VideoFrame; +} + +interface ImageDecoderInit { + colorSpaceConversion?: ColorSpaceConversion | undefined; + data: ImageBufferSource; + desiredHeight?: number | undefined; + desiredWidth?: number | undefined; + preferAnimation?: boolean | undefined; + premultiplyAlpha?: PremultiplyAlpha | undefined; + type: string; +} + +interface PlaneLayout { + offset: number; + stride: number; +} + +interface VideoColorSpaceInit { + fullRange?: boolean | null | undefined; + matrix?: VideoMatrixCoefficients | null | undefined; + primaries?: VideoColorPrimaries | null | undefined; + transfer?: VideoTransferCharacteristics | null | undefined; +} + +interface VideoDecoderConfig { + codec: string; + codedHeight?: number | undefined; + codedWidth?: number | undefined; + colorSpace?: VideoColorSpaceInit | undefined; + description?: AllowSharedBufferSource | undefined; + displayAspectHeight?: number | undefined; + displayAspectWidth?: number | undefined; + hardwareAcceleration?: HardwarePreference | undefined; + optimizeForLatency?: boolean | undefined; +} + +interface VideoDecoderInit { + error: WebCodecsErrorCallback; + output: VideoFrameOutputCallback; +} + +interface VideoDecoderSupport { + config?: VideoDecoderConfig; + supported?: boolean; +} + +interface VideoEncoderConfig { + alpha?: AlphaOption | undefined; + avc?: AvcEncoderConfig | undefined; + bitrate?: number | undefined; + bitrateMode?: VideoEncoderBitrateMode | undefined; + codec: string; + displayHeight?: number | undefined; + displayWidth?: number | undefined; + framerate?: number | undefined; + hardwareAcceleration?: HardwarePreference | undefined; + height: number; + latencyMode?: LatencyMode | undefined; + scalabilityMode?: string | undefined; + width: number; +} + +interface VideoEncoderEncodeOptions { + keyFrame?: boolean; +} + +interface VideoEncoderInit { + error: WebCodecsErrorCallback; + output: EncodedVideoChunkOutputCallback; +} + +interface VideoEncoderSupport { + config?: VideoEncoderConfig; + supported?: boolean; +} + +interface VideoFrameBufferInit { + codedHeight: number; + codedWidth: number; + colorSpace?: VideoColorSpaceInit | undefined; + displayHeight?: number | undefined; + displayWidth?: number | undefined; + duration?: number | undefined; + format: VideoPixelFormat; + layout?: PlaneLayout[] | undefined; + timestamp: number; + visibleRect?: DOMRectInit | undefined; +} + +interface VideoFrameCopyToOptions { + layout?: PlaneLayout[] | undefined; + rect?: DOMRectInit | undefined; +} + +interface VideoFrameInit { + alpha?: AlphaOption | undefined; + displayHeight?: number | undefined; + displayWidth?: number | undefined; + duration?: number | undefined; + timestamp?: number | undefined; + visibleRect?: DOMRectInit | undefined; +} + +interface AudioData { + readonly duration: number; + readonly format: AudioSampleFormat | null; + readonly numberOfChannels: number; + readonly numberOfFrames: number; + readonly sampleRate: number; + readonly timestamp: number; + allocationSize(options: AudioDataCopyToOptions): number; + clone(): AudioData; + close(): void; + copyTo( + destination: AllowSharedBufferSource, + options: AudioDataCopyToOptions, + ): void; +} + +declare var AudioData: { + prototype: AudioData; + new (init: AudioDataInit): AudioData; +}; + +interface AudioDecoderEventMap { + dequeue: Event; +} + +/** Available only in secure contexts. */ +interface AudioDecoder { + readonly decodeQueueSize: number; + readonly state: CodecState; + ondequeue: ((this: AudioDecoder, ev: Event) => any) | null; + close(): void; + configure(config: AudioDecoderConfig): void; + decode(chunk: EncodedAudioChunk): void; + flush(): Promise; + reset(): void; + addEventListener( + type: K, + listener: (this: AudioDecoder, ev: AudioDecoderEventMap[K]) => any, + options?: boolean | AddEventListenerOptions, + ): void; + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions, + ): void; + removeEventListener( + type: K, + listener: (this: AudioDecoder, ev: AudioDecoderEventMap[K]) => any, + options?: boolean | EventListenerOptions, + ): void; + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions, + ): void; +} + +declare var AudioDecoder: { + prototype: AudioDecoder; + new (init: AudioDecoderInit): AudioDecoder; + isConfigSupported(config: AudioDecoderConfig): Promise; +}; + +interface AudioEncoderEventMap { + dequeue: Event; +} + +/** Available only in secure contexts. */ +interface AudioEncoder { + readonly encodeQueueSize: number; + readonly state: CodecState; + ondequeue: ((this: AudioEncoder, ev: Event) => any) | null; + close(): void; + configure(config: AudioEncoderConfig): void; + encode(data: AudioData): void; + flush(): Promise; + reset(): void; + addEventListener( + type: K, + listener: (this: AudioEncoder, ev: AudioEncoderEventMap[K]) => any, + options?: boolean | AddEventListenerOptions, + ): void; + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions, + ): void; + removeEventListener( + type: K, + listener: (this: AudioEncoder, ev: AudioEncoderEventMap[K]) => any, + options?: boolean | EventListenerOptions, + ): void; + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions, + ): void; +} + +declare var AudioEncoder: { + prototype: AudioEncoder; + new (init: AudioEncoderInit): AudioEncoder; + isConfigSupported(config: AudioEncoderConfig): Promise; +}; + +interface EncodedAudioChunk { + readonly byteLength: number; + readonly duration: number | null; + readonly timestamp: number; + readonly type: EncodedAudioChunkType; + copyTo(destination: AllowSharedBufferSource): void; +} + +declare var EncodedAudioChunk: { + prototype: EncodedAudioChunk; + new (init: EncodedAudioChunkInit): EncodedAudioChunk; +}; + +interface EncodedVideoChunk { + readonly byteLength: number; + readonly duration: number | null; + readonly timestamp: number; + readonly type: EncodedVideoChunkType; + copyTo(destination: AllowSharedBufferSource): void; +} + +declare var EncodedVideoChunk: { + prototype: EncodedVideoChunk; + new (init: EncodedVideoChunkInit): EncodedVideoChunk; +}; + +/** Available only in secure contexts. */ +interface ImageDecoder { + readonly complete: boolean; + readonly completed: Promise; + readonly tracks: ImageTrackList; + readonly type: string; + close(): void; + decode(options?: ImageDecodeOptions): Promise; + reset(): void; +} + +// declare var ImageDecoder: { +// prototype: ImageDecoder; +// new(init: ImageDecoderInit): ImageDecoder; +// isTypeSupported(type: string): Promise; +// }; + +// interface ImageTrack { +// readonly animated: boolean; +// readonly frameCount: number; +// readonly repetitionCount: number; +// selected: boolean; +// } + +// declare var ImageTrack: { +// prototype: ImageTrack; +// new(): ImageTrack; +// }; + +// interface ImageTrackList { +// readonly length: number; +// readonly ready: Promise; +// readonly selectedIndex: number; +// readonly selectedTrack: ImageTrack | null; +// [index: number]: ImageTrack; +// } + +// declare var ImageTrackList: { +// prototype: ImageTrackList; +// new(): ImageTrackList; +// }; + +interface VideoColorSpace { + readonly fullRange: boolean | null; + readonly matrix: VideoMatrixCoefficients | null; + readonly primaries: VideoColorPrimaries | null; + readonly transfer: VideoTransferCharacteristics | null; + toJSON(): VideoColorSpaceInit; +} + +declare var VideoColorSpace: { + prototype: VideoColorSpace; + new (init?: VideoColorSpaceInit): VideoColorSpace; +}; + +interface VideoDecoderEventMap { + dequeue: Event; +} + +/** Available only in secure contexts. */ +interface VideoDecoder { + readonly decodeQueueSize: number; + readonly state: CodecState; + ondequeue: ((this: VideoDecoder, ev: Event) => any) | null; + close(): void; + configure(config: VideoDecoderConfig): void; + decode(chunk: EncodedVideoChunk): void; + flush(): Promise; + reset(): void; + addEventListener( + type: K, + listener: (this: VideoDecoder, ev: VideoDecoderEventMap[K]) => any, + options?: boolean | AddEventListenerOptions, + ): void; + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions, + ): void; + removeEventListener( + type: K, + listener: (this: VideoDecoder, ev: VideoDecoderEventMap[K]) => any, + options?: boolean | EventListenerOptions, + ): void; + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions, + ): void; +} + +declare var VideoDecoder: { + prototype: VideoDecoder; + new (init: VideoDecoderInit): VideoDecoder; + isConfigSupported(config: VideoDecoderConfig): Promise; +}; + +interface VideoEncoderEventMap { + dequeue: Event; +} + +/** Available only in secure contexts. */ +interface VideoEncoder { + readonly encodeQueueSize: number; + readonly state: CodecState; + close(): void; + ondequeue: ((this: VideoEncoder, ev: Event) => any) | null; + configure(config: VideoEncoderConfig): void; + encode(frame: VideoFrame, options?: VideoEncoderEncodeOptions): void; + flush(): Promise; + reset(): void; + addEventListener( + type: K, + listener: (this: VideoEncoder, ev: VideoEncoderEventMap[K]) => any, + options?: boolean | AddEventListenerOptions, + ): void; + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions, + ): void; + removeEventListener( + type: K, + listener: (this: VideoEncoder, ev: VideoEncoderEventMap[K]) => any, + options?: boolean | EventListenerOptions, + ): void; + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions, + ): void; +} + +declare var VideoEncoder: { + prototype: VideoEncoder; + new (init: VideoEncoderInit): VideoEncoder; + isConfigSupported(config: VideoEncoderConfig): Promise; +}; + +interface VideoFrame { + readonly codedHeight: number; + readonly codedRect: DOMRectReadOnly | null; + readonly codedWidth: number; + readonly colorSpace: VideoColorSpace; + readonly displayHeight: number; + readonly displayWidth: number; + readonly duration: number | null; + readonly format: VideoPixelFormat | null; + readonly timestamp: number; + readonly visibleRect: DOMRectReadOnly | null; + allocationSize(options?: VideoFrameCopyToOptions): number; + clone(): VideoFrame; + close(): void; + copyTo( + destination: AllowSharedBufferSource, + options?: VideoFrameCopyToOptions, + ): Promise; +} + +declare var VideoFrame: { + prototype: VideoFrame; + new (source: CanvasImageSource, init?: VideoFrameInit): VideoFrame; + new (data: AllowSharedBufferSource, init: VideoFrameBufferInit): VideoFrame; +}; + +interface AudioDataOutputCallback { + (output: AudioData): void; +} + +interface EncodedAudioChunkOutputCallback { + (output: EncodedAudioChunk, metadata: EncodedAudioChunkMetadata): void; +} + +interface EncodedVideoChunkOutputCallback { + (chunk: EncodedVideoChunk, metadata: EncodedVideoChunkMetadata): void; +} + +interface VideoFrameOutputCallback { + (output: VideoFrame): void; +} + +interface WebCodecsErrorCallback { + (error: DOMException): void; +} + +// type AllowSharedBufferSource = ArrayBuffer | ArrayBufferView; +// type BitrateMode = "constant" | "variable"; +// type ImageBufferSource = ArrayBuffer | ArrayBufferView | ReadableStream; +// type AlphaOption = "discard" | "keep"; +// type AudioSampleFormat = "f32" | "f32-planar" | "s16" | "s16-planar" | "s32" | "s32-planar" | "u8" | "u8-planar"; +// type AvcBitstreamFormat = "annexb" | "avc"; +// type CodecState = "closed" | "configured" | "unconfigured"; +// type EncodedAudioChunkType = "delta" | "key"; +// type EncodedVideoChunkType = "delta" | "key"; +type HardwarePreference = + | "no-preference" + | "prefer-hardware" + | "prefer-software"; +// type LatencyMode = "quality" | "realtime"; +// type VideoColorPrimaries = "bt470bg" | "bt709" | "smpte170m"; +// type VideoMatrixCoefficients = "bt470bg" | "bt709" | "rgb" | "smpte170m"; +// type VideoPixelFormat = "BGRA" | "BGRX" | "I420" | "I420A" | "I422" | "I444" | "NV12" | "RGBA" | "RGBX"; +// type VideoTransferCharacteristics = "bt709" | "iec61966-2-1" | "smpte170m"; diff --git a/yarn.lock b/yarn.lock index d7a49a1f..44a0d217 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4621,22 +4621,6 @@ __metadata: languageName: node linkType: hard -"@types/dom-mediacapture-transform@npm:^0.1.10": - version: 0.1.11 - resolution: "@types/dom-mediacapture-transform@npm:0.1.11" - dependencies: - "@types/dom-webcodecs": "npm:*" - checksum: 10c0/19c76d54cf31aa2a925011fc5f973dff9a10bdecfdf2285e5e568e61850a0fa2b8c9f1807a1462cbefd57ec26d32eeaa9c359117aca9d9fe7f0d6f2fff33f51e - languageName: node - linkType: hard - -"@types/dom-webcodecs@npm:*": - version: 0.1.14 - resolution: "@types/dom-webcodecs@npm:0.1.14" - checksum: 10c0/20f2e9a52986f861eb5725365561c57d4d8c7494322e55f5df1b0639e97d399c3c427e05d2bc7772c9c975197dc12db3e047e3e0ad7b49ae13181514e3d54f56 - languageName: node - linkType: hard - "@types/estree@npm:1.0.6, @types/estree@npm:^1.0.0": version: 1.0.6 resolution: "@types/estree@npm:1.0.6" @@ -6969,7 +6953,6 @@ __metadata: "@testing-library/react": "npm:^16.0.0" "@testing-library/user-event": "npm:^14.5.1" "@types/content-type": "npm:^1.1.5" - "@types/dom-mediacapture-transform": "npm:^0.1.10" "@types/grecaptcha": "npm:^3.0.9" "@types/jsdom": "npm:^21.1.7" "@types/lodash-es": "npm:^4.17.12" From fa0af6403e24a938ce123e55b49b4c0e211c6107 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 29 Apr 2025 12:21:56 +0200 Subject: [PATCH 47/51] Fix Playwright tests in Firefox As explained by the comment. --- playwright/fixtures/widget-user.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/playwright/fixtures/widget-user.ts b/playwright/fixtures/widget-user.ts index 0a422d20..d1412bd8 100644 --- a/playwright/fixtures/widget-user.ts +++ b/playwright/fixtures/widget-user.ts @@ -154,8 +154,13 @@ export const widgetTest = test.extend({ ewPage1.getByRole("heading", { name: "Invite to Welcome Room" }), ).toBeVisible(); - await ewPage1.getByRole("textbox").fill(whistlerMxId); - await ewPage1.getByRole("textbox").click(); + // To get the invite textbox we need to specifically select within the + // dialog, since there is another textbox in the background (the message + // composer). In theory the composer shouldn't be visible to Playwright at + // all because the invite dialog has trapped focus, but the focus trap + // doesn't quite work right on Firefox. + await ewPage1.getByRole("dialog").getByRole("textbox").fill(whistlerMxId); + await ewPage1.getByRole("dialog").getByRole("textbox").click(); await ewPage1.getByRole("button", { name: "Invite" }).click(); // Accept the invite From dfb9eb4b1f828bc7ceac091f5b69eedfe7aa23fe Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 29 Apr 2025 12:41:23 +0200 Subject: [PATCH 48/51] Upgrade Playwright to fix Element's unsupported browser warning --- package.json | 2 +- yarn.lock | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 3730004b..78960c8b 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@opentelemetry/sdk-trace-base": "^1.25.1", "@opentelemetry/sdk-trace-web": "^1.9.1", "@opentelemetry/semantic-conventions": "^1.25.1", - "@playwright/test": "^1.51.0", + "@playwright/test": "^1.52.0", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-slider": "^1.1.2", "@radix-ui/react-visually-hidden": "^1.0.3", diff --git a/yarn.lock b/yarn.lock index 70de0fc6..be477d19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3045,14 +3045,14 @@ __metadata: languageName: node linkType: hard -"@playwright/test@npm:^1.51.0": - version: 1.51.0 - resolution: "@playwright/test@npm:1.51.0" +"@playwright/test@npm:^1.52.0": + version: 1.52.0 + resolution: "@playwright/test@npm:1.52.0" dependencies: - playwright: "npm:1.51.0" + playwright: "npm:1.52.0" bin: playwright: cli.js - checksum: 10c0/ae83dd2c3a32133de58f44a9dbcd73a8059155ebd8acc736ba8bd0a7ca99b194afe2e8f5a500861d18b1c8f06b4e4ea8de4a2402297c59053d4becc404b47e0a + checksum: 10c0/1c428b421593eb4f79b7c99783a389c3ab3526c9051ec772749f4fca61414dfa9f2344eba846faac5f238084aa96c836364a91d81d3034ac54924f239a93e247 languageName: node linkType: hard @@ -6860,7 +6860,7 @@ __metadata: "@opentelemetry/sdk-trace-base": "npm:^1.25.1" "@opentelemetry/sdk-trace-web": "npm:^1.9.1" "@opentelemetry/semantic-conventions": "npm:^1.25.1" - "@playwright/test": "npm:^1.51.0" + "@playwright/test": "npm:^1.52.0" "@radix-ui/react-dialog": "npm:^1.0.4" "@radix-ui/react-slider": "npm:^1.1.2" "@radix-ui/react-visually-hidden": "npm:^1.0.3" @@ -10295,27 +10295,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.51.0": - version: 1.51.0 - resolution: "playwright-core@npm:1.51.0" +"playwright-core@npm:1.52.0": + version: 1.52.0 + resolution: "playwright-core@npm:1.52.0" bin: playwright-core: cli.js - checksum: 10c0/8f5de23088c5e97c00327f356b17e0223181e921baf99f4e38d9a3b18d0693db288f8b5389e96d0cb4a1b55f03870f140dd7346128a0c02ce36d11eb92153841 + checksum: 10c0/640945507e6ca2144e9f596b2a6ecac042c2fd3683ff99e6271e9a7b38f3602d415f282609d569456f66680aab8b3c5bb1b257d8fb63a7fc0ed648261110421f languageName: node linkType: hard -"playwright@npm:1.51.0": - version: 1.51.0 - resolution: "playwright@npm:1.51.0" +"playwright@npm:1.52.0": + version: 1.52.0 + resolution: "playwright@npm:1.52.0" dependencies: fsevents: "npm:2.3.2" - playwright-core: "npm:1.51.0" + playwright-core: "npm:1.52.0" dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: 10c0/e8509ea500e03e8051fd243f2347ac3196ff8dde4c20ae3aba4cf723e2b647a0158d209fba062995dab90590229a483d723562cf1ea8b2fc11698617027416fd + checksum: 10c0/2c6edf1e15e59bbaf77f3fa0fe0ac975793c17cff835d9c8b8bc6395a3b6f1c01898b3058ab37891b2e4d424bcc8f1b4844fe70d943e0143d239d7451408c579 languageName: node linkType: hard From 9f5eae0ac104fd0d90c27e50617d79eb7c8c73fa Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 29 Apr 2025 13:02:02 +0200 Subject: [PATCH 49/51] Clean up a string --- locales/en/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en/app.json b/locales/en/app.json index ab3f8dd0..d185d2f8 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -166,7 +166,7 @@ }, "background_blur_header": "Background", "background_blur_label": "Blur the background of the video", - "blur_not_supported_by_browser": "(Background blur is not supported by this device)", + "blur_not_supported_by_browser": "(Background blur is not supported by this device.)", "developer_tab_title": "Developer", "devices": { "camera": "Camera", From eeb3a1a9914f4eb16a20f7de3d5baed5c07c3e07 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 29 Apr 2025 22:19:00 +0200 Subject: [PATCH 50/51] Add license notices to inlined type declarations --- src/@types/dom-mediacapture-transform.d.ts | 9 +++++++++ src/@types/dom-webcodecs.d.ts | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/@types/dom-mediacapture-transform.d.ts b/src/@types/dom-mediacapture-transform.d.ts index ac55dc90..d4c1f8f8 100644 --- a/src/@types/dom-mediacapture-transform.d.ts +++ b/src/@types/dom-mediacapture-transform.d.ts @@ -8,6 +8,15 @@ // depending on @types/dom-mediacapture-transform. // ----------------------------------------------------------------------------- +// This project is licensed under the MIT license. +// Copyrights are respective of each contributor listed at the beginning of each definition file. + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + // In general, these types are only available behind a command line flag or an origin trial in // Chrome 90+. diff --git a/src/@types/dom-webcodecs.d.ts b/src/@types/dom-webcodecs.d.ts index f06ceedd..55e4c7d4 100644 --- a/src/@types/dom-webcodecs.d.ts +++ b/src/@types/dom-webcodecs.d.ts @@ -7,6 +7,15 @@ // @types/dom-webcodecs. // ----------------------------------------------------------------------------- +// This project is licensed under the MIT license. +// Copyrights are respective of each contributor listed at the beginning of each definition file. + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + // Versioning: // Until the WebCodecs spec is finalized, the major version number is 0. I have chosen to use minor // version 1 to denote the API as defined by the IDL files from the Chromium repo at From aee30d84022771dc1aed5ef3d21e558d03ec809a Mon Sep 17 00:00:00 2001 From: Timo <16718859+toger5@users.noreply.github.com> Date: Fri, 2 May 2025 11:49:49 +0200 Subject: [PATCH 51/51] Fix release notes (#3244) --- .github/{realease.yml => release.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{realease.yml => release.yml} (100%) diff --git a/.github/realease.yml b/.github/release.yml similarity index 100% rename from .github/realease.yml rename to .github/release.yml