Ok, entonces la pregunta es un poco exagerada, pero en realidad menciona cada parte del "error" que estoy experimentando. No estoy seguro si es realmente un error o si hay algo que no entiendo todavía. Creé una demostración en codepen para reproducir el comportamiento extraño.
https://codepen.io/rroyerrivard/pen/jOwBLbB
Como escribí en el codepen, parece haber un error en Chrome por el cual la secuencia de un HTMLCanvasElement no se actualiza en cada dibujo de imagen cuando está oculto. Pero para que eso suceda, es necesario que existan estas 4 condiciones a la vez.
HTMLVideoElement con un MediaStream obtenido de una llamada a HTMLCanvasElement.captureStream() (en lugar de mostrar directamente el HTMLCanvasElement ).HTMLCanvasElement del que obtenemos el MediaStream debe estar oculto (ya sea no en el DOM o tenerlo oculto con css).HTMLCanvasElement desde un OffscreenCanvas que obtenemos de una llamada a HTMLCanvasElement.transferControlToOffscreen() .OffscreenCanvas debe realizarse en un trabajador web al que se transfirió OffscreenCanvas . Tuve la mala suerte de cumplir con todas estas condiciones a la vez en una aplicación web en el trabajo. Puedo evitar el error al no usar la llamada transferControlToOffscreen() y dibujar un ImageBitmap en el hilo principal después de recibirlo del trabajador web, pero eso reduce el FPS en aproximadamente un 18%.
¿Es esto un error conocido? ¿Hay alguna manera de forzar que MediaStream se actualice en cada sorteo de OffscreenCanvas ?
Supongo que es el comportamiento esperado, sí.
Lo que pasa aquí es que hizo que su subproceso de trabajo esperara usando setTimeout y MessageEvents del subproceso principal.
OffscreenCanvas confirmará su mapa de bits en el lienzo de marcador de posición en el marco de representación del trabajador. Pero, de forma predeterminada, Worker no ingresará a este marco de representación. Debe solicitarlo mediante requestAnimationFrame .
Tener el marcador de posición visible en la página hará internamente la solicitud de que OffscreenCanvas confirme su mapa de bits cuando el lienzo del marcador de posición se represente (es decir, en el marco de representación del hilo principal), es por eso que funciona cuando el lienzo del marcador de posición es visible.
Tenga en cuenta que solíamos tener un método OffscreenCanvas.commit() pero quedó obsoleto cuando requestAnimationFrame apareció en WorkerContexts.
Por lo tanto, use requestAnimationFrame en su Worker para forzar la confirmación del mapa de bits en el lienzo del marcador de posición.
const video = document.querySelector("video"); const select = document.querySelector("select"); const canvas = document.createElement("canvas"); const offscreen = canvas.transferControlToOffscreen(); const worker = new Worker(getWorkerURL()); worker.postMessage({ offscreen }, [offscreen]); select.oninput = e => worker.postMessage({ method: select.value }); worker.onmessage = (evt) => { video.srcObject = canvas.captureStream(); }; function getWorkerURL() { return URL.createObjectURL( new Blob([ document.querySelector("[type='text/worker']").textContent ], { type: "text/javascript" }) ); } canvas { border: 1px solid } <video controls autoplay></video> <label>waiting method:<select><option>rAF</option><option>setTimeout</option></select></label> <script type="text/worker"> let ctx; let waiting_method = "rAF"; onmessage = ({ data: { offscreen, method } }) => { if (offscreen) { ctx = offscreen.getContext("2d"); draw(); postMessage("ready"); } else if (method) { waiting_method = method; } }; function draw() { ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.fillText(new Date().getTime(), 30, 30); if (waiting_method === "rAF") { requestAnimationFrame(draw); } else { setTimeout(draw, 1000/30); } } </script> Ahora, supongo que también podríamos esperar que la llamada a captureStream() active la misma solicitud interna de compromiso que activa el lienzo de marcador de posición visible, por lo que es posible que desee presentar un problema en https://crbug.com independientemente.