MediaRecorder for Real Recording Pipelines: Chunked Capture, WebM, and the Safari Problem
MediaRecorder is the bridge between 'camera demo' and 'recorded file' — chunked data handling, codec selection, duration limits, and the Safari fallback strategy.
MediaRecorder turns a MediaStream into a file — but “turns into a file” hides the real work: chunks arrive asynchronously, the container format is WebM (or MP4 on newer Safari), and a 10-minute recording in memory will crash the tab if you don’t stream it out.
The Basic Pipeline
const recorder = new MediaRecorder(stream, {
mimeType: 'video/webm;codecs=vp9,opus',
videoBitsPerSecond: 2500000,
audioBitsPerSecond: 128000
});
const chunks = [];
recorder.ondataavailable = (e) => { if (e.data.size) chunks.push(e.data); };
recorder.onstop = () => {
const blob = new Blob(chunks, { type: 'video/webm' });
upload(blob); // or download, or store
};
recorder.start(1000); // emit a data chunk every second — critical for long recordings
The timeslice argument to start(1000) is the difference between a demo and a pipeline: without it, dataavailable only fires on stop() — a 5-minute recording buffers entirely in RAM.
Codec and Container Selection
mimeType | Browser Support | Notes |
|---|---|---|
video/webm;codecs=vp9,opus | Chrome, Edge, Firefox | Best quality/bitrate |
video/webm;codecs=vp8,opus | Chrome, Firefox | Broader VP8 compatibility |
video/mp4 | Safari 14.1+ (partial), Chrome | H.264 inside — easier to edit |
Safari is the outlier — it supports MediaRecorder since 14.1 but defaults to video/mp4 and can produce files that some tools refuse to edit. If your pipeline feeds into a standard NLE or transcoder, force a WebM-compatible fallback or transcode on upload.
The Duration Trap
A 10-minute 1080p30 VP9 recording at 2.5 Mbps produces ~190 MB of chunks. In-memory buffering is fine on a desktop; on a phone it OOMs. For production:
- Upload in chunks as
dataavailablefires — send each Blob to your server, don’t accumulate. - Or use a Service Worker to write chunks to IndexedDB, then upload in background when the user is done.
“The
start(1000)timeslice is the single line that separates a recording demo from a recording feature. Everything else is error handling.”
Chunked upload protocol, resume logic, and the Safari MP4 transcode path are in the MediaRecorder production pipeline.