The rise of video name purposes in lots of domains additionally comes with some privateness concerns. A typical situation value taking a look at is display sharing.
How can we forestall mistakenly sharing delicate materials throughout display sharing in Telehealth, E-learning, and plenty of different use circumstances?
Zoom supplies a sophisticated screen-sharing performance that doesn’t include the common browser screen-share dialog.
I’ll stroll you thru methods to implement comparable performance which could be later built-in into your primary Video Name software or answer.
Click here to see the full source code.
Deciding on the Display screen to Share
We use the browser media gadgets API to convey up the display choice dialog. After this, we deal with the choice of a portion of curiosity utilizing CropperJs.
async operate startCapture() {
strive {
captureStream = await navigator.mediaDevices.getDisplayMedia();
const imageUrl = await getImageFromVideoStream(captureStream);
const imgElem = doc.getElementById("previewImage");
imgElem.type.show = "block";
imgElem.src = imageUrl;
const videoDisplay = doc.getElementById("screenshareDisplay");
videoDisplay.type.show = "block";
new Cropper(imgElem, {
zoomable: false,
restore: false,
crop(occasion) {
coordinates.x = occasion.element.x;
coordinates.y = occasion.element.y;
coordinates.top = occasion.element.top;
coordinates.width = occasion.element.width;
const generatedStream = processVideoTrack(captureStream);
videoDisplay.srcObject = generatedStream;
},
});
} catch (err) {
console.error(`Error: ${err}`);
}
}
Getting the Coordinates of the Space of Curiosity
To get the portion we wish to share, we seize a picture from the display we chosen and go it to the Cropper Library. This fashion we will get the coordinates that replicate the chosen display.
async operate getImageFromVideoStream(stream) {
const canvas = doc.createElement("canvas");
if ("ImageCapture" in window) {
const videoTrack = stream.getVideoTracks()[0];
const imageCapture = new window.ImageCapture(videoTrack);
const bitmap = await imageCapture.grabFrame();
canvas.top = bitmap.top;
canvas.width = bitmap.width;
canvas.getContext("second").drawImage(bitmap, 0, 0);
return canvas.toDataURL();
}
const video = doc.createElement("video");
video.srcObject = stream;
return new Promise((resolve, reject) => {
video.addEventListener("loadeddata", async () => {
const { videoWidth, videoHeight } = video;
canvas.width = videoWidth;
canvas.top = videoHeight;
strive {
await video.play();
canvas
.getContext("second")
.drawImage(video, 0, 0, videoWidth, videoHeight);
return resolve(canvas.toDataURL());
} catch (error) {
return reject(error);
}
});
});
}
The picture is then handed to the Cropper occasion. At this level, we show the field for cropping the world of curiosity on the picture. Dragging the field updates the coordinates of the chosen space which we use to crop the video stream from the display share, body by body.
new Cropper(imgElem, {
zoomable: false,
restore: false,
crop(occasion) {
coordinates.x = occasion.element.x;
coordinates.y = occasion.element.y;
coordinates.top = occasion.element.top;
coordinates.width = occasion.element.width;
const generatedStream = processVideoTrack(captureStream);
videoDisplay.srcObject = generatedStream;
},
});
Cropping the Video monitor from the Screenshare
That is the place we see the insertable stream at play. We modify every body by cropping to the chosen display portion coordinates we get from the cropper.
// Cropping every body
operate cropVideoFramesWithCoordinates(body, controller) {
const newFrame = new window.VideoFrame(body, {
visibleRect: {
x: getSampleAlignedCoordinates(Math.spherical(coordinates.x)),
y: getSampleAlignedCoordinates(Math.spherical(coordinates.y)),
width: getSampleAlignedCoordinates(Math.spherical(coordinates.width)),
top: getSampleAlignedCoordinates(Math.spherical(coordinates.top)),
},
});
controller.enqueue(newFrame);
body.shut();
}
// Insertable stream logic
operate processVideoTrack(monitor) {
const mainTrack = monitor.getVideoTracks()[0] ?? monitor;
const generator = new window.MediaStreamTrackGenerator({
type: "video",
});
const generatedStream = new window.MediaStream([generator]);
const processor = new window.MediaStreamTrackProcessor({
monitor: mainTrack,
});
processor.readable
.pipeThrough(
new window.TransformStream({
rework: cropVideoFramesWithCoordinates,
})
)
.pipeTo(generator.writable)
.catch((err) => {
// TODO: Determine methods to forestall this error
console.log("pipe error: ", { err });
});
return generatedStream;
}
Demo
Conclusion
This exhibits you the overall method to reaching partial display share as we’ve got in Zoom. You’ll be able to at all times enhance the UX to fit your use case.
In subsequent articles, I’ll exhibit how this may be built-in into present video name platforms.