2021-11-21 19:43:40 +00:00
|
|
|
import { MediaSet, MediaSetServiceClientImpl } from './generated/media_set';
|
|
|
|
import { newRPC } from './App';
|
2021-10-08 14:38:35 +00:00
|
|
|
import { useEffect, useRef } from 'react';
|
|
|
|
|
|
|
|
interface Props {
|
2021-11-21 19:43:40 +00:00
|
|
|
mediaSet: MediaSet;
|
2021-10-08 14:38:35 +00:00
|
|
|
position: number;
|
|
|
|
duration: number;
|
|
|
|
height: number;
|
|
|
|
video: HTMLVideoElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const VideoPreview: React.FC<Props> = ({
|
2021-11-21 19:43:40 +00:00
|
|
|
mediaSet,
|
2021-10-08 14:38:35 +00:00
|
|
|
position,
|
|
|
|
duration,
|
|
|
|
height,
|
|
|
|
video,
|
|
|
|
}: Props) => {
|
|
|
|
const videoCanvasRef = useRef<HTMLCanvasElement>(null);
|
|
|
|
|
|
|
|
// effects
|
|
|
|
|
2021-11-21 19:43:40 +00:00
|
|
|
// 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]);
|
|
|
|
|
2021-10-08 14:38:35 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
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]);
|
|
|
|
|
|
|
|
// render component
|
|
|
|
|
|
|
|
const containerStyles = {
|
|
|
|
height: height + 'px',
|
|
|
|
position: 'relative',
|
|
|
|
flexGrow: 0,
|
|
|
|
} as React.CSSProperties;
|
|
|
|
|
|
|
|
const canvasStyles = {
|
|
|
|
position: 'absolute',
|
|
|
|
width: '100%',
|
|
|
|
height: '100%',
|
|
|
|
display: 'block',
|
|
|
|
zIndex: 1,
|
|
|
|
} as React.CSSProperties;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div style={containerStyles}>
|
|
|
|
<canvas
|
|
|
|
width="500"
|
|
|
|
height="100"
|
|
|
|
ref={videoCanvasRef}
|
|
|
|
style={canvasStyles}
|
|
|
|
></canvas>
|
|
|
|
<canvas style={canvasStyles}></canvas>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|