mirror of https://github.com/mifi/lossless-cut
Implement a canvas for better playback of more formats #88
parent
25b9a04dde
commit
b1bd2731b4
@ -0,0 +1,33 @@
|
||||
|
||||
import React, { memo, useEffect, useRef, useMemo } from 'react';
|
||||
|
||||
import CanvasPlayer from './CanvasPlayer';
|
||||
|
||||
const Canvas = memo(({ rotate, filePath, width, height, playerTime, commandedTime, playing }) => {
|
||||
const canvasRef = useRef();
|
||||
|
||||
const canvasPlayer = useMemo(() => CanvasPlayer({ path: filePath, width, height }),
|
||||
[filePath, width, height]);
|
||||
|
||||
useEffect(() => {
|
||||
canvasPlayer.setCanvas(canvasRef.current);
|
||||
|
||||
return () => {
|
||||
canvasPlayer.setCanvas();
|
||||
if (canvasPlayer) canvasPlayer.dispose();
|
||||
};
|
||||
}, [canvasPlayer]);
|
||||
|
||||
useEffect(() => {
|
||||
if (playing) canvasPlayer.play(commandedTime);
|
||||
else canvasPlayer.pause(playerTime);
|
||||
}, [canvasPlayer, commandedTime, playerTime, playing]);
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: '100%', left: 0, right: 0, top: 0, bottom: 0, position: 'absolute', overflow: 'hidden', background: 'black' }}>
|
||||
<canvas ref={canvasRef} style={{ display: 'block', width: '100%', height: '100%', objectFit: 'contain', transform: `rotate(${rotate}deg)` }} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Canvas;
|
||||
@ -0,0 +1,105 @@
|
||||
import { encodeLiveRawStream, getOneRawFrame } from './ffmpeg';
|
||||
|
||||
// TODO keep everything in electron land?
|
||||
const strtok3 = window.require('strtok3');
|
||||
|
||||
export default ({ path, width: inWidth, height: inHeight }) => {
|
||||
let canvas;
|
||||
|
||||
let terminated;
|
||||
let cancel;
|
||||
let commandedTime;
|
||||
let playing;
|
||||
|
||||
function drawOnCanvas(rgbaImage, width, height) {
|
||||
if (!canvas || rgbaImage.length === 0) return;
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/ImageData/ImageData
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData
|
||||
ctx.putImageData(new ImageData(Uint8ClampedArray.from(rgbaImage), width, height), 0, 0);
|
||||
}
|
||||
|
||||
async function run() {
|
||||
let process;
|
||||
let cancelled;
|
||||
|
||||
cancel = () => {
|
||||
cancelled = true;
|
||||
if (process) process.cancel();
|
||||
cancel = undefined;
|
||||
};
|
||||
|
||||
if (playing) {
|
||||
try {
|
||||
const { process: processIn, channels, width, height } = encodeLiveRawStream({ path, inWidth, inHeight, seekTo: commandedTime });
|
||||
process = processIn;
|
||||
|
||||
// process.stderr.on('data', data => console.log(data.toString('utf-8')));
|
||||
|
||||
const tokenizer = await strtok3.fromStream(process.stdout);
|
||||
|
||||
const size = width * height * channels;
|
||||
const buf = Buffer.allocUnsafe(size);
|
||||
|
||||
while (!cancelled) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await tokenizer.readBuffer(buf, { length: size });
|
||||
if (!cancelled) drawOnCanvas(buf, width, height);
|
||||
}
|
||||
} catch (err) {
|
||||
if (!err.isCanceled) console.warn(err.message);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const { process: processIn, width, height } = getOneRawFrame({ path, inWidth, inHeight, seekTo: commandedTime });
|
||||
process = processIn;
|
||||
const { stdout: rgbaImage } = await process;
|
||||
|
||||
if (!cancelled) drawOnCanvas(rgbaImage, width, height);
|
||||
} catch (err) {
|
||||
if (!err.isCanceled) console.warn(err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function command() {
|
||||
if (cancel) cancel();
|
||||
run();
|
||||
}
|
||||
|
||||
function pause(seekTo) {
|
||||
if (terminated) return;
|
||||
if (!playing && commandedTime === seekTo) return;
|
||||
playing = false;
|
||||
commandedTime = seekTo;
|
||||
command();
|
||||
}
|
||||
|
||||
function play(playFrom) {
|
||||
if (terminated) return;
|
||||
if (playing && commandedTime === playFrom) return;
|
||||
playing = true;
|
||||
commandedTime = playFrom;
|
||||
command();
|
||||
}
|
||||
|
||||
function setCanvas(c) {
|
||||
canvas = c;
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
terminated = true;
|
||||
if (cancel) cancel();
|
||||
}
|
||||
|
||||
return {
|
||||
play,
|
||||
pause,
|
||||
setCanvas,
|
||||
dispose,
|
||||
};
|
||||
};
|
||||
Loading…
Reference in New Issue