Improve compatibility

This commit is contained in:
2025-12-10 18:12:28 +01:00
parent 43af334762
commit 009ecdc2ca

View File

@@ -1,4 +1,7 @@
const canvas = document.getElementById('broadcast-canvas'); const canvas = document.getElementById('broadcast-canvas');
const obsBrowser = !!window.obsstudio;
const supportsAnimatedDecode = typeof ImageDecoder !== 'undefined' && typeof createImageBitmap === 'function' && !obsBrowser;
const canPlayProbe = document.createElement('video');
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
let canvasSettings = { width: 1920, height: 1080 }; let canvasSettings = { width: 1920, height: 1080 };
canvas.width = canvasSettings.width; canvas.width = canvasSettings.width;
@@ -313,6 +316,11 @@ function isDrawable(element) {
} }
function clearMedia(assetId) { function clearMedia(assetId) {
const element = mediaCache.get(assetId);
if (isVideoElement(element)) {
element.src = '';
element.remove();
}
mediaCache.delete(assetId); mediaCache.delete(assetId);
const animated = animatedCache.get(assetId); const animated = animatedCache.get(assetId);
if (animated) { if (animated) {
@@ -335,6 +343,8 @@ function clearMedia(assetId) {
} }
audio.element.pause(); audio.element.pause();
audio.element.currentTime = 0; audio.element.currentTime = 0;
audio.element.src = '';
audio.element.remove();
audioControllers.delete(assetId); audioControllers.delete(assetId);
} }
} }
@@ -398,7 +408,6 @@ function getAssetVolume(asset) {
function applyMediaVolume(element, asset) { function applyMediaVolume(element, asset) {
if (!element) return 1; if (!element) return 1;
const volume = getAssetVolume(asset); const volume = getAssetVolume(asset);
element.muted = volume === 0;
element.volume = Math.min(volume, 1); element.volume = Math.min(volume, 1);
return volume; return volume;
} }
@@ -509,7 +518,7 @@ function ensureMedia(asset) {
return null; return null;
} }
if (isGifAsset(asset) && 'ImageDecoder' in window) { if (isGifAsset(asset) && supportsAnimatedDecode) {
const animated = ensureAnimatedImage(asset); const animated = ensureAnimatedImage(asset);
if (animated) { if (animated) {
mediaCache.set(asset.id, animated); mediaCache.set(asset.id, animated);
@@ -521,6 +530,9 @@ function ensureMedia(asset) {
element.dataset.sourceUrl = asset.url; element.dataset.sourceUrl = asset.url;
element.crossOrigin = 'anonymous'; element.crossOrigin = 'anonymous';
if (isVideoElement(element)) { if (isVideoElement(element)) {
if (!canPlayVideoType(asset.mediaType)) {
return null;
}
element.loop = true; element.loop = true;
element.playsInline = true; element.playsInline = true;
element.autoplay = true; element.autoplay = true;
@@ -529,9 +541,8 @@ function ensureMedia(asset) {
element.onloadedmetadata = () => recordDuration(asset.id, element.duration); element.onloadedmetadata = () => recordDuration(asset.id, element.duration);
element.preload = 'auto'; element.preload = 'auto';
element.addEventListener('error', () => clearMedia(asset.id)); element.addEventListener('error', () => clearMedia(asset.id));
const volume = applyMediaVolume(element, asset); applyMediaVolume(element, asset);
element.defaultMuted = volume === 0; element.muted = true;
element.muted = element.defaultMuted;
setVideoSource(element, asset); setVideoSource(element, asset);
} else { } else {
element.onload = draw; element.onload = draw;
@@ -611,6 +622,11 @@ function fetchAssetBlob(asset) {
} }
function setVideoSource(element, asset) { function setVideoSource(element, asset) {
if (!shouldUseBlobUrl(asset)) {
applyVideoSource(element, asset.url, asset);
return;
}
const cached = blobCache.get(asset.id); const cached = blobCache.get(asset.id);
if (cached?.url === asset.url && cached.objectUrl) { if (cached?.url === asset.url && cached.objectUrl) {
applyVideoSource(element, cached.objectUrl, asset); applyVideoSource(element, cached.objectUrl, asset);
@@ -631,6 +647,18 @@ function applyVideoSource(element, objectUrl, asset) {
startVideoPlayback(element, asset); startVideoPlayback(element, asset);
} }
function shouldUseBlobUrl(asset) {
return !obsBrowser && asset?.mediaType && canPlayVideoType(asset.mediaType);
}
function canPlayVideoType(mediaType) {
if (!mediaType) {
return true;
}
const support = canPlayProbe.canPlayType(mediaType);
return support === 'probably' || support === 'maybe';
}
function getCachedSource(element) { function getCachedSource(element) {
return element?.dataset?.sourceUrl || element?.src; return element?.dataset?.sourceUrl || element?.src;
} }
@@ -687,33 +715,25 @@ function startVideoPlayback(element, asset) {
element.playbackRate = effectiveSpeed; element.playbackRate = effectiveSpeed;
} }
const volume = applyMediaVolume(element, asset); const volume = applyMediaVolume(element, asset);
element.defaultMuted = volume === 0; const shouldUnmute = volume > 0;
element.muted = element.defaultMuted; element.muted = true;
if (effectiveSpeed === 0) { if (effectiveSpeed === 0) {
element.pause(); element.pause();
return; return;
} }
const attemptPlay = (allowFallback) => { element.play();
const playPromise = element.play();
if (playPromise?.then) {
playPromise.then(() => {
if (volume > 0) {
element.muted = false;
}
}).catch(() => {
if (!allowFallback) {
queueAudioForUnlock({ element });
return;
}
element.muted = true;
element.play().catch(() => queueAudioForUnlock({ element }));
});
}
};
attemptPlay(true); if (shouldUnmute) {
if (!element.paused && element.readyState >= 2) {
element.muted = false;
} else {
element.addEventListener('playing', () => {
element.muted = false;
}, { once: true });
}
}
} }
function startRenderLoop() { function startRenderLoop() {