Avoid opaque canvases in case of Firefox (fixes #117)

This commit is contained in:
Vanilagy
2025-09-16 17:42:23 +02:00
parent 530ccd7523
commit 1bb3c0e262
4 changed files with 44 additions and 9 deletions
+10 -3
View File
@@ -18,6 +18,7 @@ import {
getInt24,
getUint24,
insertSorted,
isFirefox,
isSafari,
last,
mapAsyncGenerator,
@@ -1199,14 +1200,20 @@ export class CanvasSink {
this._nextCanvasIndex = (this._nextCanvasIndex + 1) % this._canvasPool.length;
}
const context
= canvas.getContext('2d', { alpha: false }) as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
const context = canvas.getContext('2d', {
alpha: isFirefox(), // Firefox has VideoFrame glitches with opaque canvases
}) as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
assert(context);
context.resetTransform();
if (!canvasIsNew) {
context.clearRect(0, 0, this._width, this._height);
if (isFirefox()) {
context.fillStyle = 'black';
context.fillRect(0, 0, this._width, this._height);
} else {
context.clearRect(0, 0, this._width, this._height);
}
}
sample.drawWithFit(context, {
+19 -4
View File
@@ -18,7 +18,16 @@ import {
VideoCodec,
} from './codec';
import { OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack } from './output';
import { assert, assertNever, CallSerializer, clamp, promiseWithResolvers, setInt24, setUint24 } from './misc';
import {
assert,
assertNever,
CallSerializer,
clamp,
isFirefox,
promiseWithResolvers,
setInt24,
setUint24,
} from './misc';
import { Muxer } from './muxer';
import { SubtitleParser } from './subtitles';
import { toAlaw, toUlaw } from './pcm';
@@ -248,12 +257,18 @@ class VideoEncoderWrapper {
canvasIsNew = true;
}
const context = this.resizeCanvas.getContext('2d', { alpha: false }) as
CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
const context = this.resizeCanvas.getContext('2d', {
alpha: isFirefox(), // Firefox has VideoFrame glitches with opaque canvases
}) as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
assert(context);
if (!canvasIsNew) {
context.clearRect(0, 0, this.codedWidth, this.codedHeight);
if (isFirefox()) {
context.fillStyle = 'black';
context.fillRect(0, 0, this.codedWidth, this.codedHeight);
} else {
context.clearRect(0, 0, this.codedWidth, this.codedHeight);
}
}
videoSample.drawWithFit(context, { fit: sizeChangeBehavior });
+9
View File
@@ -642,6 +642,15 @@ export const isSafari = () => {
return result;
};
let isFirefoxCache: boolean | null = null;
export const isFirefox = () => {
if (isFirefoxCache !== null) {
return isFirefoxCache;
}
return isFirefoxCache = typeof navigator !== 'undefined' && navigator.userAgent?.includes('Firefox');
};
/**
* T or a promise that resolves to T.
* @group Miscellaneous
+6 -2
View File
@@ -15,6 +15,7 @@ import {
toDataView,
toUint8Array,
SetRequired,
isFirefox,
} from './misc';
/**
@@ -228,7 +229,10 @@ export class VideoSample {
}
const canvas = new OffscreenCanvas(width, height);
const context = canvas.getContext('2d', { alpha: false, willReadFrequently: true });
const context = canvas.getContext('2d', {
alpha: isFirefox(), // Firefox has VideoFrame glitches with opaque canvases
willReadFrequently: true,
});
assert(context);
// Draw it to a canvas
@@ -343,7 +347,7 @@ export class VideoSample {
dest.set(this._data);
} else {
const canvas = this._data;
const context = canvas.getContext('2d', { alpha: false });
const context = canvas.getContext('2d');
assert(context);
const imageData = context.getImageData(0, 0, this.codedWidth, this.codedHeight);