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