# HG changeset patch # User Arne Babenhauserheide # Date 1640654094 -3600 # Tue Dec 28 02:14:54 2021 +0100 # Node ID a71d7802216d66ce6d7bb50bf57cc9a6414d845c # Parent 9121e129547ac5dc262a52527e5f4a9f7fa1763b update m3u-player diff --git a/site/m3u-player.js b/site/m3u-player.js --- a/site/m3u-player.js +++ b/site/m3u-player.js @@ -102,6 +102,44 @@ }; xhr.send(); } + +function showStaticOverlay(mediaTag, canvas) { + if (mediaTag instanceof Audio) { + return; + } + // take screenshot of video and overlay it to mask short-term flicker. + const realWidth = mediaTag.getBoundingClientRect().width; + const realHeight = mediaTag.getBoundingClientRect().height; + canvas.width = realWidth; + canvas.height = realHeight; + // need the actual video size + const videoAspectRatio = mediaTag.videoHeight / mediaTag.videoWidth; + const tagAspectRatio = realHeight / realWidth; + const videoIsPartialHeight = tagAspectRatio > (videoAspectRatio * 1.01); // avoid rounding errors + const videoIsPartialWidth = videoAspectRatio > (tagAspectRatio * 1.01); // avoid rounding errors + if (videoIsPartialHeight) { + canvas.height = realWidth * videoAspectRatio; + } else if (videoIsPartialWidth) { + canvas.width = realHeight / videoAspectRatio; + } + const context = canvas.getContext("2d"); + context.scale(canvas.width / mediaTag.videoWidth, canvas.height / mediaTag.videoHeight); + context.drawImage(mediaTag, 0, 0); + canvas.hidden = true; + mediaTag.parentNode.insertBefore(canvas, mediaTag.nextSibling); + canvas.style.position = "absolute"; + // shift canvas to cover only the space where the video actually is + if (videoIsPartialWidth) { + canvas.style.marginLeft = "-" + ((realWidth + canvas.width) / 2.) + "px"; + } else { + canvas.style.marginLeft = "-" + realWidth + "px"; + } + if (videoIsPartialHeight) { + canvas.style.marginTop = ((realHeight - canvas.height) / 2.) + "px"; + } + canvas.hidden = false; +} + function updateSrc(mediaTag, callback) { const playlistUrl = mediaTag.getAttribute("playlist"); const trackIndex = mediaTag.getAttribute("track-index"); @@ -132,26 +170,25 @@ const oldUrl = mediaTag.getAttribute("src"); // prevent size flickering by setting height before src change const canvas = document.createElement("canvas"); - if (!isNaN(mediaTag.duration)) { // already loaded a valid file so the size should fit - // fix height to the height of the current video. Re-run after setting the source. - mediaTag.height = (mediaTag.clientWidth * mediaTag.videoHeight) / mediaTag.videoWidth; - // take screenshot of video and overlay it to mask flicker - canvas.width = mediaTag.clientWidth; - canvas.height = mediaTag.clientHeight; - const context = canvas.getContext("2d"); - context.scale(mediaTag.clientWidth / mediaTag.videoWidth, mediaTag.clientHeight / mediaTag.videoHeight); - context.drawImage(mediaTag, 0, 0); - canvas.hidden = true; - mediaTag.parentNode.insertBefore(canvas, mediaTag.nextSibling); - canvas.style.position = "absolute"; - canvas.style.marginLeft = "-" + mediaTag.clientWidth + "px"; - canvas.hidden = false; + if (!isNaN(mediaTag.duration) // already loaded a valid file + && document.fullscreen !== true) { // overlay does not work for fullscreen + // mask flickering with a static overlay + try { + showStaticOverlay(mediaTag, canvas); + } catch (error) { + console.log(error); + } } + // force sizes to stay constant during loading of the next segment + mediaTag.style.height = mediaTag.getBoundingClientRect().height.toString() + 'px'; + mediaTag.style.width = mediaTag.getBoundingClientRect().width.toString() + 'px'; + // swich to the next segment mediaTag.setAttribute("src", url); mediaTag.oncanplaythrough = () => { if (!isNaN(mediaTag.duration)) { // already loaded a valid file - // fix height to the height of the current video. Re-run after setting the source. - mediaTag.height = (mediaTag.clientWidth * mediaTag.videoHeight) / mediaTag.videoWidth; + // unset element styles to allow recomputation if sizes changed + mediaTag.style.height = null; + mediaTag.style.width = null; } // remove overlay canvas.hidden = true;