En vivo: el desenfoque de fondo siempre falla en Brave Browser ("No se pudo iniciar el desenfoque de fondo") - .play() de <video> desconectado se interrumpe

Resumen

FAO: @Falco :smiling_face_with_sunglasses:
Divulgación de IA: Este error fue investigado de forma autónoma y la causa y la solución fueron sugeridas por Opus 5.5.

En Brave Browser, activar el desenfoque de fondo en una sala Live siempre muestra un mensaje de error: “No se pudo iniciar el desenfoque de fondo, por lo que se desactivó.” El mismo usuario, foro y sala funcionan bien en Google Chrome en el mismo equipo.

error: "Background blur couldn't be started, so it was turned off."

La causa es el elemento <video> oculto que BackgroundBlurManager#setup() crea pero nunca inserta en el documento. Brave rechaza play() en él con:

AbortError: The play() request was interrupted because the media was removed from the document.

Insertar el elemento en el documento (de forma invisible) antes de play() hace que funcione en Brave.

Entorno

  • Discourse 2026.10.0-latest.1 (568a006ad8), plugin Live integrado, discourse_voice_assets 0.2.0
  • Brave (Chromium 155) en Linux, Wayland, GPU Intel; aceleración por hardware para WebGL2 y WebGPU
  • Google Chrome en el mismo equipo: funciona

Pasos para reproducir

  1. Abrir una sala Live en Brave y encender la cámara.
  2. Activar el desenfoque de fondo.
  3. Aparece el mensaje y el desenfoque se desactiva.

Diagnóstico

Ejecuté el propio pipeline de desenfoque del plugin paso a paso en la consola de Brave (misma ruta de recursos, modelo, delegado y modo de ejecución que background-blur.js), con la protección contra huellas digitales de Brave activada y luego desactivada:

Paso Brave, protección contra huellas digitales activada Brave, protección contra huellas digitales desactivada
Importación de vision_bundle.js, carga de WASM ok ok
ImageSegmenter.createFromOptions, delegados GPU y CPU ok ok
getUserMedia({ video: true }), estado de la pista ok, live ok, live
play() en un <video> silenciado y desconectado con el flujo de la cámara (lo que hace el plugin) AbortError AbortError
play() en un <video> del mismo tipo agregado a document.body (1px, transparente) ok ok

Por lo tanto, MediaPipe, WebGL y la cámara están todos bien. Solo falla el play() desconectado, y la protección contra huellas digitales de Brave no marca diferencia. Chromium sin interfaz gráfica reproduce un <video> desconectado sin error, lo cual coincide con que Chrome funcione.

Solución sugerida

En plugins/live/assets/javascripts/discourse/lib/live/background-blur.js, en setup(): agrega el video oculto al documento antes de video.play(), y elimínalo en teardown(). Por ejemplo:

video.setAttribute("aria-hidden", "true");
video.style.cssText =
  "position:fixed;width:1px;height:1px;opacity:0;pointer-events:none";
document.body.appendChild(video);
// ... await video.play() como ahora ...

// teardown():
this.#video?.remove();

Por lo que puedo ver, este es el único lugar en el plugin donde se reproduce un elemento multimedia desconectado.

Dos observaciones relacionadas

  1. El error real se ignora. #createBackgroundBlur() en local-video-manager.js usa un catch {} vacío y llama a liveLog.warn("[live] failed to start background blur") sin el error. Incluso con live_verbose_logging habilitado, la consola nunca dice por qué falló el desenfoque. Pasar el error capturado a liveLog.warn (que ya lo sanitiza) habría hecho que el error emergiera más rápidamente.

  2. Se ofrece el desenfoque donde nunca puede funcionar. BackgroundBlurManager.isSupported() solo comprueba canvas.captureStream y WebAssembly. En un navegador sin WebGL, el segmentador de MediaPipe falla con ambos delegados GPU y CPU (Service "kGpuService" ... cannot be created: emscripten_webgl_create_context() returned error 0), porque el posprocesador de segmentación necesita un contexto GL de todas formas. Agregar una comprobación de WebGL2 a isSupported() ocultaría o deshabilitaría el control de desenfoque en lugar de fallar después de que el usuario lo active. (Reproducido en Chromium sin interfaz gráfica simulando getContext("webgl*"), no visto en el mundo real.)

Gracias por Live - aparte de esto, nos está funcionando bien.

1 me gusta

A primera vista, parece bien. ¿Querías enviar un PR con la corrección?