From af51d79502584604d5de72168d65a82f52df58f4 Mon Sep 17 00:00:00 2001 From: Maxim Devaev Date: Tue, 13 May 2025 19:56:48 +0300 Subject: [PATCH] web: Workaround Direct H.264 flickering on Firefox --- web/share/js/kvm/stream_media.js | 58 +++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/web/share/js/kvm/stream_media.js b/web/share/js/kvm/stream_media.js index 269929f1..a67a020f 100644 --- a/web/share/js/kvm/stream_media.js +++ b/web/share/js/kvm/stream_media.js @@ -38,8 +38,9 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) { var __ping_timer = null; var __missed_heartbeats = 0; - var __decoder = null; var __codec = ""; + var __decoder = null; + var __frame = null; var __canvas = $("stream-canvas"); var __ctx = __canvas.getContext("2d"); @@ -208,7 +209,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) { __closeDecoder(); __codec = codec; __decoder = new VideoDecoder({ // eslint-disable-line no-undef - "output": __drawFrame, + "output": __renderFrame, "error": (err) => __logInfo(err.message), }); if (started) { @@ -241,24 +242,41 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) { if (__decoder !== null) { try { __decoder.close(); - } catch { - // Pass } finally { - __decoder = null; __codec = ""; + __decoder = null; + if (__frame !== null) { + try { + __closeFrame(__frame); + } finally { + __frame = null; + } + } } } }; - var __drawFrame = function(frame) { + var __renderFrame = function(frame) { + if (__frame === null) { + __frame = frame; + window.requestAnimationFrame(__drawPendingFrame, __canvas); + } else { + __closeFrame(frame); + } + }; + + var __drawPendingFrame = function() { + if (__frame === null) { + return; + } try { - let width = frame.displayWidth; - let height = frame.displayHeight; + let width = __frame.displayWidth; + let height = __frame.displayHeight; switch (__orient) { case 90: case 270: - width = frame.displayHeight; - height = frame.displayWidth; + width = __frame.displayHeight; + height = __frame.displayWidth; } if (__canvas.width !== width || __canvas.height !== height) { @@ -267,7 +285,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) { } if (__orient === 0) { - __ctx.drawImage(frame, 0, 0); + __ctx.drawImage(__frame, 0, 0); } else { __ctx.save(); try { @@ -276,14 +294,28 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) { case 180: __ctx.translate(width, height); __ctx.rotate(-Math.PI); break; case 270: __ctx.translate(width, 0); __ctx.rotate(Math.PI / 2); break; } - __ctx.drawImage(frame, 0, 0); + __ctx.drawImage(__frame, 0, 0); } finally { __ctx.restore(); } } - __fps_accum += 1; } finally { + __closeFrame(__frame); + __frame = null; + } + }; + + var __closeFrame = function(frame) { + if (!tools.browser.is_firefox) { + // FIXME: On Firefox, image is flickering when we're closing the frame for some reason. + // So we're just not performing the close() and it seems there is no problems here + // because Firefox is implementing some auto-closing logic. With auto-close, + // no flickering observed. + // - https://github.com/mozilla/gecko-dev/blob/82333a9/dom/media/webcodecs/VideoFrame.cpp + // Note at 2025.05.13: + // - The problem is not observed on nightly Firefox 140. + // - It's also not observed with hardware accelleration on 138. frame.close(); } };