@@ -2031,48 +2031,57 @@ <h4>Members</h4>
20312031 < section >
20322032 < h3 > Example</ h3 >
20332033 < pre class ="example ">
2034+ // main.js:
20342035// Open camera.
20352036const stream = await navigator.mediaDevices.getUserMedia({video: true});
2036- const [videoTrack] = stream.getVideoTracks();
2037+ const [track] = stream.getVideoTracks();
2038+ // Do video processing in a worker.
2039+ const worker = new Worker('worker.js');
2040+ worker.postMessage({track}, [track]);
2041+ const {data} = await new Promise(result => worker.onmessage = result);
2042+ const videoElement = document.querySelector('video');
2043+ videoElement.srcObject = new MediaStream([data.track]);
20372044
2038- // Try to enable background segmentation mask.
2039- const videoCapabilities = videoTrack.getCapabilities();
2040- if ((videoCapabilities.backgroundSegmentationMask || []).includes(true)) {
2041- await videoTrack.applyConstraints({backgroundSegmentationMask: {exact: true}});
2042- } else {
2043- // Background segmentation mask is not supported by the platform or
2044- // by the camera. Consider falling back to some other method.
2045- }
2045+ // worker.js:
2046+ onmessage = async ({data: {track}}) => {
2047+ // Try to enable background segmentation mask.
2048+ const capabilities = track.getCapabilities();
2049+ if (capabilities.backgroundSegmentationMask?.includes(true)) {
2050+ await track.applyConstraints({backgroundSegmentationMask: {exact: true}});
2051+ } else {
2052+ // Background segmentation mask is not supported by the platform or
2053+ // by the camera. Consider falling back to some other method.
2054+ }
2055+ const trackGenerator = new VideoTrackGenerator();
2056+ self.postMessage({track: trackGenerator.track}, [trackGenerator.track]);
2057+ const {readable} = new MediaStreamTrackProcessor({track});
20462058
2047- const canvasContext = document.querySelector('canvas').getContext('2d');
2048- const videoProcessor = new MediaStreamTrackProcessor({track: videoTrack});
2049- const videoProcessorReader = videoProcessor.readable.getReader();
2059+ const canvas = new OffscreenCanvas(640, 480);
2060+ const context = canvas.getContext('2d', {desynchronized: true});
20502061
2051- for (;;) {
2052- const {done, value: videoFrame} = await videoProcessorReader.read();
2053- if (done)
2054- break;
2055- const {backgroundSegmentationMask} = videoFrame.metadata();
2056- if (backgroundSegmentationMask) {
2057- // Draw the video frame.
2058- canvasContext.globalCompositeOperation = 'copy';
2059- context.drawImage(videoFrame, 0, 0);
2060- // Draw (or multiply with) the mask.
2061- // The result is the foreground on black.
2062- context.globalCompositeOperation = 'multiply';
2063- canvasContext.drawImage(backgroundSegmentationMask, 0, 0);
2064- }
2065- else {
2066- // Everything is background. Fill with black.
2067- canvasContext.globalCompositeOperation = 'copy';
2068- canvasContext.fillStyle = 'black';
2069- canvasContext.fillRect(
2070- 0,
2071- 0,
2072- canvasContext.canvas.width,
2073- canvasContext.canvas.height);
2074- }
2075- }
2062+ const transformer = new TransformStream({
2063+ async transform(frame, controller) {
2064+ const {backgroundSegmentationMask} = frame.metadata();
2065+ if (backgroundSegmentationMask) {
2066+ // Draw the video frame.
2067+ context.globalCompositeOperation = 'copy';
2068+ context.drawImage(frame, 0, 0);
2069+ // Draw (or multiply with) the mask.
2070+ // The result is the foreground on black.
2071+ context.globalCompositeOperation = 'multiply';
2072+ context.drawImage(backgroundSegmentationMask, 0, 0);
2073+ } else {
2074+ // Everything is background. Fill with black.
2075+ context.globalCompositeOperation = 'copy';
2076+ context.fillStyle = 'black';
2077+ context.fillRect(0, 0, canvas.width, canvas.height);
2078+ }
2079+ controller.enqueue(new VideoFrame(canvas, {timestamp: frame.timestamp}));
2080+ frame.close();
2081+ }
2082+ });
2083+ await readable.pipeThrough(transformer).pipeTo(trackGenerator.writable);
2084+ };
20762085 </ pre >
20772086 </ section >
20782087 </ section >
0 commit comments