From 8a69a07cc792469775b73acbff7431ef5592f1df Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Mon, 22 Nov 2021 19:20:40 +0100 Subject: [PATCH] Refactor frontend thumbnail handling --- frontend/src/VideoPreview.tsx | 108 +++++++++++++--------------------- 1 file changed, 42 insertions(+), 66 deletions(-) diff --git a/frontend/src/VideoPreview.tsx b/frontend/src/VideoPreview.tsx index cb96b20..8abd30f 100644 --- a/frontend/src/VideoPreview.tsx +++ b/frontend/src/VideoPreview.tsx @@ -21,82 +21,58 @@ export const VideoPreview: React.FC = ({ // effects - // load thumbnail, to display when the component is loaded for the first - // time. This is needed because of browser autoplay limitations. - useEffect(() => { - (async function () { - if (mediaSet == null) { - return; - } - - const canvas = videoCanvasRef.current; - if (canvas == null) { - console.error('no canvas ref available'); - return; - } - - const ctx = canvas.getContext('2d'); - if (ctx == null) { - console.error('no 2d context available'); - return; - } - - // Set aspect ratio. - canvas.width = canvas.height * (canvas.clientWidth / canvas.clientHeight); - - console.log('getting video thumbnail...'); - const rpc = newRPC(); - const service = new MediaSetServiceClientImpl(rpc); - - const thumbnail = await service.GetVideoThumbnail({ id: mediaSet.id }); - - console.log('got thumbnail', thumbnail); - - const url = URL.createObjectURL( - new Blob([thumbnail.image], { type: 'image/jpeg' }) - ); - const img = new Image(thumbnail.width, thumbnail.height); - - img.src = url; - console.log('img', img); - img.onerror = console.error; - img.onload = () => { - ctx.drawImage(img, 0, 0, 177, 100); - }; - - console.log('set src to', url); - })(); - }, [mediaSet]); - // render canvas useEffect(() => { // TODO: not sure if requestAnimationFrame is recommended here. requestAnimationFrame(() => { - const canvas = videoCanvasRef.current; - if (canvas == null) { - console.error('no canvas ref available'); - return; - } + (async function () { + const canvas = videoCanvasRef.current; + if (canvas == null) { + console.error('no canvas ref available'); + return; + } - const ctx = canvas.getContext('2d'); - if (ctx == null) { - console.error('no 2d context available'); - return; - } + const ctx = canvas.getContext('2d'); + if (ctx == null) { + console.error('no 2d context available'); + return; + } - // Set aspect ratio. - canvas.width = canvas.height * (canvas.clientWidth / canvas.clientHeight); + // Set aspect ratio. + canvas.width = + canvas.height * (canvas.clientWidth / canvas.clientHeight); - const durSecs = duration / 1000; - const ratio = position / durSecs; - const x = (canvas.width - 177) * ratio; + // If the required position is 0, display the thumbnail instead of + // trying to render the video. The most important use case is before a + // click event has happened, when autoplay restrictions will prevent + // the video being rendered to canvas. + if (position == 0) { + const service = new MediaSetServiceClientImpl(newRPC()); + const thumbnail = await service.GetVideoThumbnail({ + id: mediaSet.id, + }); - ctx.clearRect(0, 0, x, canvas.height); - ctx.clearRect(x + 177, 0, canvas.width - 177 - x, canvas.height); + // TODO: avoid fetching the image every re-render: + const url = URL.createObjectURL( + new Blob([thumbnail.image], { type: 'image/jpeg' }) + ); + const img = new Image(thumbnail.width, thumbnail.height); - ctx.drawImage(video, x, 0, 177, 100); + img.src = url; + img.onload = () => ctx.drawImage(img, 0, 0, 177, 100); + return; + } + + // otherwise, render the video, which (should) work now. + const durSecs = duration / 1000; + const ratio = position / durSecs; + const x = (canvas.width - 177) * ratio; + ctx.clearRect(0, 0, x, canvas.height); + ctx.clearRect(x + 177, 0, canvas.width - 177 - x, canvas.height); + ctx.drawImage(video, x, 0, 177, 100); + })(); }); - }, [position]); + }, [mediaSet, position]); // render component