Website to buzz at preset time in meetings
Released: Virtual Webcam
While scrolling internal feed at workplace portal, I came across a hackathon project published by a couple of FB engineers internally. It used a virtual-webcam GitHub repo to provide a chrome extension which can mask the camera input to support certain effects, like overlay Gifs, stickers, dummy video, etc. Although the internal hack was to support some other things, I thought to try and build out what I conceived. My vision is to explore and understand how camera input can be intercepted and replaced with a pre-recorded video. I would play along with multiple things in the journey.
Goals
In short, Following are the reasons I decided to fork and play around with Virtual Webcam:
Understand how to intercept camera input
Understand how to add effects to the camera stream
Understand the basics of building chrome extensions
Understand testing chrome extensions
As POC, I made a simple website to achieve these: Virtual Webcam. I had observed that I may need to experiment more before it can be called as usable!
Flow
How to intercept camera input:
// override the MediaDevices.getUserMedia() API const enumerateDevicesFn = MediaDevices.prototype.enumerateDevices; const getUserMediaFn = MediaDevices.prototype.getUserMedia; MediaDevices.prototype.enumerateDevices = async function () { const res = await enumerateDevicesFn.call(navigator.mediaDevices); // We could add "Virtual VHS" or "Virtual Median Filter" and map devices with filters. res.push({ deviceId: "virtual", groupID: "uh", kind: "videoinput", label: "Virtual Chrome Webcam", }); return res; }; MediaDevices.prototype.getUserMedia = async function () { const res = await getUserMediaFn.call(navigator.mediaDevices, ...arguments); //// Intercepted here!! return res; }; How to add effects to the camera stream:
// camera input const video = document.createElement("video"); const canvas = document.createElement("canvas"); this.canvas = canvas; this.renderer = new ShaderRenderer(this.canvas, video, shader); video.addEventListener("playing", () => { // Use a 2D Canvas. // this.canvas.width = this.video.videoWidth; // this.canvas.height = this.video.videoHeight; // Use a WebGL Renderer. this.renderer.setSize(this.video.videoWidth, this.video.videoHeight); this.update(); }); video.srcObject = stream; video.autoplay = true; this.video = video; //this.ctx = this.canvas.getContext('2d'); this.outputStream = this.canvas.captureStream(); Basics of building chrome extensions:
// having a manifest.json but cannot load any external resource { "name": "Virtual Webcam", "version": "1.0.0", "minimum_chrome_version": "10.0", "description": "Virtual webcam on all chrome tabs", "default_popup": "popup.html" }, "content_scripts": [ { "matches": [ " " ], "js": [ "js/inject.js" ], "run_at": "document_start", "all_frames": true } ], "permissions": [ "tabs", "storage", "tabCapture", " ", "http://*/*", "https://*/*" ], "manifest_version": 2, "web_accessible_resources": [ "js/*" ] } </xmp></pre> Testing chrome extensions:
I needed to upload the manifest.json in chrome://extensions
Load unpacked
option and all files were picked up automatically on the go.
I felt it was not so straightforward to implement and it had a lot of dependencies to slow me down, especially around Chrome Extensions, Canvas, WebGL, etc. Having a simple website to overlay camera video with effects worked fine for me, but I am yet to test how it would perform for browser-based video calls.
For my demo, the source is hosted on GitHub repo: Virtual Webcam repo, quick sneak peek is below:
TECH
javascript website chrome