In this pandemic, a lot has changed with work culture. Something we can’t get rid of yet is Remote Meetings. While at one of those meetings, I got curious about the Google meeting platform and after some search came to know what our browsers are capable of (inbuilt browser APIs).
So, I planned to develop a free simple recorder that supports Audio, Video, and Screen Recording available at http://recorder.dhruv479.dev
To proceed further, you should be acquainted with some terms:
MediaRecorder: The MediaRecorder interface of the MediaStream API provides functionality to easily record media ref: MDN
MediaDevices: The MediaDevices interface provides access to connected media input devices like cameras and microphones, as well as screen-sharing ref: MDN
Ok, ready to roll? let’s add some code!
Recording Handler: At first, we will write a function that intakes the stream and mimeType, collect the data chunks and then enable downloading the file on the local machine from the browser.
const handleRecord = function ({stream, mimeType}) {
// to collect stream chunks
let recordedChunks = [];
stopped = false;
const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = function (e) {
if (e.data.size > 0) {
recordedChunks.push(e.data);
}
// shouldStop => forceStop by user
if (shouldStop === true && stopped === false) {
mediaRecorder.stop();
stopped = true;
}
};
mediaRecorder.onstop = function () {
const blob = new Blob(recordedChunks, {
type: mimeType
});
recordedChunks = []
const filename = window.prompt('Enter file name'); // input filename from user for download
downloadLink.href = URL.createObjectURL(blob); // create download link for the file
downloadLink.download = `${filename}.webm`; // naming the file with user provided name
stopRecord();
};
mediaRecorder.start(200); // here 200ms is interval of chunk collection
};
First, create a MediaRecorder instance with the input stream. Then, add events for ondataavailable (when data comes in-stream) and onstop (when the stream stops).
After chunk is available, it is pushed in the array and after the stop event, all the chunks are put together to form a Blob specifying the mimeType.
Later, URL is created for the blob object and can be downloaded from the browser.
Audio Recorder: Going forward, we will write a function to invoke the audio recording that further calls handleRecord with the required parameters.
async function recordAudio() {
const mimeType = 'audio/webm';
shouldStop = false;
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
handleRecord({stream, mimeType})
}
Here, we will use mediaDevices.getUserMedia to get access to resources on the user’s machine. After calling this, we will get a prompt asking for permission in the browser and after confirmation, we can access those resources.
Also, we can pass additional parameters to the constraints like audio: { echoCancellation: true } which reduces the sound echo.
Video Recorder: Similar to Audio Recording, we need to add video in the parameter of the getUserMedia function.
async function recordVideo() {
const mimeType = 'video/webm';
shouldStop = false;
const constraints = {
audio: true,
video: true,
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
handleRecord({stream, mimeType})
}
After adding video constraint in getUserMedia, you will get an additional prompt to camera permission and after the confirmation, guess what, we can access the camera stream. Also, additional params can be passed to video such as: video: { width: {min: 640}, height: {min: 480}}
Screen Recorder: Amongst all, this is the most interesting, as the display stream is provided by getDisplayMedia, and the audio is provided by getUserMedia. So, we need to merge different streams here if need an audio background for the screen record.
async function recordScreen() {
const mimeType = 'video/webm';
shouldStop = false;
const constraints = {
video: true
};
const displayStream = await navigator.mediaDevices.getDisplayMedia({video: true, audio: true});
// voiceStream for recording voice with screen recording
const voiceStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
let tracks = [...displayStream.getTracks(), ...voiceStream.getAudioTracks()]
const stream = new MediaStream(tracks);
handleRecord({stream, mimeType})
}
We merged the different streams to create a new stream and passed it to the handleRecord function. Here also, we can add more specific constraints as per the need.
Constraints passed to capture media can be more specific like resolution, echo, cursor instead of true/false.
Voila, well the JavaScript part is done 😁. We just need to add some basic
HTML to invoke those functions, stop recording, and of course, download
the file.
Here is a reference to the complete code:
https://github.com/dhruv479/recorder/blob/master/index.html
Also published at https://dhruv479.medium.com/583584dd3c75