Article MSTP + VTG spec
by jib1
HTML
<button id="start">Start</button> works in Safari 18 Tech Preview<br><br>
<video id="video1" width="320" height="240" autoplay muted></video>
<video id="video2" width="320" height="240" autoplay muted></video><br>
JavaScript
start.onclick = async () => {
video1.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
const track = video1.srcObject.getVideoTracks()[0].clone();
const worker = new Worker(`data:text/javascript,(${work.toString()})()`);
worker.postMessage({track}, [track]);
const {data} = await new Promise(r => worker.onmessage = r);
video2.srcObject = new MediaStream([data.track]);
};
function work() {
onmessage = async ({data: {track}}) => {
const vtg = new VideoTrackGenerator();
self.postMessage({track: vtg.track}, [vtg.track]);
const mstp = new MediaStreamTrackProcessor({track});
await mstp.readable.pipeThrough(new TransformStream({transform})).pipeTo(vtg.writable);
};
const canvas = new OffscreenCanvas(640, 480);
const ctx = canvas.getContext('2d', {desynchronized: true});
ctx.translate(canvas.width, 0);
ctx.scale(-1, 1);
function transform(frame, controller) {
ctx.drawImage(frame, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const {data} = imageData;
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i + 1], b = data[i + 2];
data[i] = 0.393 * r + 0.769 * g + 0.189 * b;
data[i + 1] = 0.349 * r + 0.686 * g + 0.168 * b;
data[i + 2] = 0.272 * r + 0.534 * g + 0.131 * b;
}
ctx.putImageData(imageData, 0, 0);
controller.enqueue(new VideoFrame(canvas, {timestamp: frame.timestamp}));
frame.close();
}
}