From 1089d874afb061eaad56cdeb4a4daf8aa53d00f8 Mon Sep 17 00:00:00 2001 From: Max Ronzhin Date: Mon, 10 Aug 2026 13:16:01 +0300 Subject: [PATCH] =?UTF-8?q?fix(room):=20=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D1=84=D0=BE=D0=BD=D0=B0=20=D0=B2=20Safari=20=D0=B8=20?= =?UTF-8?q?Firefox?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проверка поддержки была строже, чем требует библиотека, и молча отрезала два браузера: кнопки выбора фона не было ни в превью, ни в комнате. У `@livekit/track-processors` ДВА конвейера обработки кадров: современный (`MediaStreamTrackProcessor`/`Generator`, только Chrome и производные) и запасной — рисует кадры в canvas и отдаёт `canvas.captureStream()`. Проверка требовала именно современный, хотя запасной путь доступен и в Safari, и в Firefox. Заодно она проверяла `OffscreenCanvas`, но пропускала `VideoFrame`, `createImageBitmap` и WebGL2, которые библиотеке реально нужны. Теперь условие буквально повторяет `supportsBackgroundProcessors()` из самой библиотеки: «умеет считать сегментацию» И «есть хоть какой-то конвейер». При обновлении пакета сверять с ним. Пробный WebGL2-контекст (иначе поддержку не определить) создаётся один раз на жизнь страницы и сразу отпускается через `WEBGL_lose_context`: браузеры держат ограниченное число живых контекстов. Проверено: в Firefox 153 детект возвращает «поддерживается» (современного конвейера нет, запасной есть), в Chrome регрессии нет — процессор поднимается, трек живой. --- .gitignore | 1 + frontend/src/lib/virtualBackground.ts | 61 ++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 795daa5..3fe3be3 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ frontend/public/mediapipe/wasm/ # Рабочая страница замера CPU (сессия 35), в репозиторий не идёт. frontend/bg-bench.html +frontend/bg-support.html diff --git a/frontend/src/lib/virtualBackground.ts b/frontend/src/lib/virtualBackground.ts index 1cd05fd..dbe64f8 100644 --- a/frontend/src/lib/virtualBackground.ts +++ b/frontend/src/lib/virtualBackground.ts @@ -98,21 +98,60 @@ export function resolveBackgroundUrl( } /** - * Поддерживает ли браузер сегментацию вообще. + * Поддерживает ли браузер замену фона. * - * Проверяется по наличию API конвейера обработки кадров (Insertable Streams / - * `MediaStreamTrackProcessor`) — их нет, например, в Firefox. Проверка - * СИНХРОННАЯ и намеренно не трогает саму библиотеку: она нужна до её загрузки, - * чтобы не показывать кнопку, которая всё равно не сработает, и не тянуть - * мегабайты впустую. + * Проверка СИНХРОННАЯ и намеренно не трогает саму библиотеку: решение нужно + * ДО её загрузки, чтобы не показывать кнопку, которая не сработает, и не + * тянуть мегабайты впустую. Поэтому здесь буквально повторено условие + * `supportsBackgroundProcessors()` из `@livekit/track-processors` — при + * обновлении пакета сверять с ним. + * + * ⚠️ Конвейеров у библиотеки ДВА, и требовать современный нельзя: + * - современный (`MediaStreamTrackProcessor`/`Generator`, Insertable Streams) + * есть только в Chrome и производных; + * - запасной рисует кадры в canvas и отдаёт `canvas.captureStream()` — он + * работает в Safari и Firefox. + * + * Первая версия (0.0.35) требовала именно современный конвейер — и фича + * молча отсутствовала в Safari и Firefox, хотя запасной путь там доступен. + * Условие ниже — «умеет считать сегментацию» И «есть хоть какой-то конвейер». */ export function isVirtualBackgroundSupported(): boolean { if (typeof window === 'undefined') return false - return ( - 'MediaStreamTrackProcessor' in window && - 'MediaStreamTrackGenerator' in window && - typeof OffscreenCanvas !== 'undefined' - ) + return canRunSegmentation() && hasAnyFramePipeline() +} + +/** + * Условие `BackgroundTransformer.isSupported`: чем библиотека считает маску и + * собирает кадр. WebGL2 проверяется созданием пробного контекста — иначе никак, + * но результат кэшируется на всю жизнь страницы: браузеры держат ограниченное + * число живых WebGL-контекстов, и создавать новый на каждый рендер нельзя. + */ +let segmentationSupport: boolean | null = null +function canRunSegmentation(): boolean { + if (segmentationSupport !== null) return segmentationSupport + const hasApis = + typeof OffscreenCanvas !== 'undefined' && + typeof VideoFrame !== 'undefined' && + typeof createImageBitmap !== 'undefined' + if (!hasApis) { + segmentationSupport = false + return false + } + const probe = document.createElement('canvas').getContext('webgl2') + // Пробный контекст сразу отпускаем — он больше не нужен, а слот в лимите + // браузера занимал бы до сборки мусора. + probe?.getExtension('WEBGL_lose_context')?.loseContext() + segmentationSupport = Boolean(probe) + return segmentationSupport +} + +/** Условие `ProcessorWrapper.isSupported`: современный конвейер ЛИБО запасной на canvas. */ +function hasAnyFramePipeline(): boolean { + const modern = 'MediaStreamTrackProcessor' in window && 'MediaStreamTrackGenerator' in window + const fallback = + typeof HTMLCanvasElement !== 'undefined' && 'captureStream' in HTMLCanvasElement.prototype + return modern || fallback } /**